huridocs/uwazi

View on GitHub
scripts/emitSchemaTypes.js

Summary

Maintainability
A
0 mins
Test Coverage
/* eslint-disable import/first,global-require,import/no-dynamic-require,no-console */
require('dotenv').config();
require('@babel/register')({ extensions: ['.js', '.jsx', '.ts', '.tsx'] });

const fs = require('fs');
const path = require('path');
const { compile } = require('json-schema-to-typescript');

const rootPath = '..';

const opts = {
  strictIndexSignatures: true,
  enableConstEnums: false,
  declareExternallyReferenced: false,
  bannerComment: '',
  style: {
    singleQuote: true,
  },
};
const banner = '/* eslint-disable */\n/**AUTO-GENERATED. RUN yarn emit-types to update.*/\n';

const customImports = {
  '../app/shared/types/commonSchemas.ts': [
    "import { ObjectId } from 'mongodb';",
    "import { TraverseInputType } from 'shared/types/relationshipsQueryTypes'",
  ],
  '../app/api/common.v2/database/schemas/commonSchemas.ts': ["import { ObjectId } from 'mongodb';"],
  '../app/shared/types/connectionSchema.ts': ["import { FileType } from 'shared/types/fileType';"],
};

const dryCheck = !!process.argv[2] && process.argv[2] === '--check';

const firstUp = name => name.charAt(0).toUpperCase() + name.slice(1);
const typesFileName = file =>
  file.replace('Schema', 'Type').replace('.js', '.ts').replace('.ts', '.d.ts');
const typeImportRegex = /import[^;]*from '(.*Schemas?)';/;
const typeImportFindRegex = /import[^;]*from '(.*Schemas?)';/g;

const typeImports = matches => {
  if (!(matches && matches.length)) {
    return '';
  }
  return matches.reduce((res, match) => {
    const file = match.match(typeImportRegex)[1];
    const typeFile = typesFileName(file);
    const schemas = require(path.join(`${rootPath}/app`, file));
    let final = match.replace(file, typeFile);
    Object.entries(schemas).forEach(([name, schema]) => {
      if (name.match(/Schema/)) {
        final = final.replace(name, schema.title || firstUp(name));
      }
    });
    return `${res}\n${final}\n`;
  }, '');
};

const checkTypeFile = (file, content) => {
  const endProcess = () => {
    console.error(`Must emit types: ${file} changed`);
    process.exit(1);
  };

  if (fs.existsSync(path.join(__dirname, file))) {
    const oldContent = fs.readFileSync(path.join(__dirname, file)).toString();

    if (oldContent !== content) endProcess();
  } else {
    endProcess();
  }
};

const writeTypeFile = (file, commonImport, snippets) => {
  const goodSnippets = snippets.filter(p => p);
  if (goodSnippets.length) {
    const typeFile = typesFileName(file);
    const customImport = customImports[file] ? `${customImports[file].join('\n')}\n` : '';
    if (!dryCheck) {
      console.log(`Emitting ${goodSnippets.length} types from ${file} to ${typeFile}.`);
    }

    const content =
      banner + customImport + commonImport + goodSnippets.reduce((res, s) => `${res}\n${s}`, '');

    if (dryCheck) {
      checkTypeFile(typeFile, content);
    } else {
      fs.writeFileSync(path.join(__dirname, typeFile), content);
    }
  }
};

const writeSchema = async (schemas, file) => {
  const snippets = await Promise.all(
    Object.entries(schemas).map(([name, schema]) => {
      if (!name.match(/Schema$/)) {
        return '';
      }
      return compile(schema, schema.title || firstUp(name), opts);
    })
  );

  const contents = fs.readFileSync(path.join(__dirname, file)).toString();
  writeTypeFile(file, typeImports(contents.match(typeImportFindRegex)), snippets);
};

const emitSchemaTypes = async file => {
  try {
    if (file.match(/spec/)) {
      return;
    }

    if (file.match(/shared\/types/) || file.match(/Schema/)) {
      const schemas = require(file);
      if (!schemas.emitSchemaTypes) {
        return;
      }
      writeSchema(schemas, file);
    }
  } catch (err) {
    console.error(`Failed emitting types from ${file}: ${err}.`);
  }
};

function walk(dir, callback) {
  fs.readdir(dir, (err, files) => {
    if (err) throw err;
    files.forEach(file => {
      const filepath = path.join(dir, file);
      fs.stat(filepath, (err2, stats) => {
        if (err2) throw err2;
        if (stats.isDirectory()) {
          walk(filepath, callback);
        } else if (stats.isFile()) {
          callback(path.join(rootPath, filepath), stats);
        }
      });
    });
  });
}

walk('./app', emitSchemaTypes);