diff --git a/package-lock.json b/package-lock.json
index 9b88f508..103ba38c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,14 @@
"resolved": "https://registry.npmjs.org/@types/circular-json/-/circular-json-0.4.0.tgz",
"integrity": "sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg=="
},
+ "@types/del": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/del/-/del-3.0.1.tgz",
+ "integrity": "sha512-y6qRq6raBuu965clKgx6FHuiPu3oHdtmzMPXi8Uahsjdq1L6DL5fS/aY5/s71YwM7k6K1QIWvem5vNwlnNGIkQ==",
+ "requires": {
+ "@types/glob": "*"
+ }
+ },
"@types/events": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
@@ -203,6 +211,19 @@
"integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=",
"dev": true
},
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
+ },
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@@ -668,6 +689,19 @@
"type-detect": "^4.0.0"
}
},
+ "del": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
+ "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=",
+ "requires": {
+ "globby": "^6.1.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "p-map": "^1.1.1",
+ "pify": "^3.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
@@ -1046,6 +1080,25 @@
"path-is-absolute": "^1.0.0"
}
},
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
+ }
+ },
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
@@ -1176,6 +1229,27 @@
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0="
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
@@ -1557,8 +1631,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
- "dev": true
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
@@ -1621,6 +1694,11 @@
"p-limit": "^1.1.0"
}
},
+ "p-map": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
+ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA=="
+ },
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
@@ -1652,6 +1730,11 @@
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+ },
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@@ -1679,20 +1762,17 @@
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
},
"pinkie": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
- "dev": true
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
},
"pinkie-promise": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
- "dev": true,
"requires": {
"pinkie": "^2.0.0"
}
@@ -1842,7 +1922,6 @@
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
- "dev": true,
"requires": {
"glob": "^7.1.3"
}
@@ -2066,6 +2145,11 @@
"os-tmpdir": "~1.0.1"
}
},
+ "toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA="
+ },
"trim-newlines": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz",
diff --git a/package.json b/package.json
index 1d228b44..7fe004e1 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"@openstapps/logger": "0.0.3",
"@types/async": "2.0.50",
"@types/chai": "4.1.7",
+ "@types/del": "3.0.1",
"@types/glob": "7.1.1",
"@types/humanize-string": "1.0.0",
"@types/mocha": "5.2.5",
@@ -49,12 +50,14 @@
"async-pool-native": "0.1.0",
"chai": "4.2.0",
"commander": "2.19.0",
+ "del": "3.0.0",
"glob": "7.1.3",
"humanize-string": "1.0.2",
"jsonschema": "1.2.4",
"mocha": "5.2.0",
"mocha-typescript": "1.1.17",
"mustache": "3.0.1",
+ "toposort": "2.0.2",
"ts-json-schema-generator": "0.38.1",
"ts-node": "7.0.1",
"typedoc": "0.14.0"
diff --git a/src/cli.ts b/src/cli.ts
index aa7c8af2..46513106 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018-2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -15,7 +15,8 @@
import * as commander from 'commander';
import {existsSync, readFileSync, writeFileSync} from 'fs';
import {join, resolve} from 'path';
-import {getProjectReflection, logger, mkdirPromisified, readFilePromisifed} from './common';
+import {getProjectReflection, logger, mkdirPromisified, readFilePromisified} from './common';
+import {pack} from './pack';
import {gatherRouteInformation, generateDocumentationForRoute, getNodeMetaInformationMap} from './routes';
import {Converter, getValidatableTypesFromReflection} from './schema';
import {validateFiles, writeReport} from './validate';
@@ -91,7 +92,7 @@ commander
logger.info(`Using ${corePackageJsonPath} to determine version for schemas.`);
- const buffer = await readFilePromisifed(corePackageJsonPath);
+ const buffer = await readFilePromisified(corePackageJsonPath);
const corePackageJson = JSON.parse(buffer.toString());
const coreVersion = corePackageJson.version;
@@ -141,6 +142,12 @@ commander
}
});
+commander
+ .command('pack')
+ .action(async () => {
+ await pack();
+ });
+
commander
.parse(process.argv);
diff --git a/src/common.ts b/src/common.ts
index 2e517d1b..16645f87 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -13,22 +13,20 @@
* this program. If not, see .
*/
import {Logger} from '@openstapps/logger';
-import {mkdir, PathLike, readFile, writeFile} from 'fs';
+import {mkdir, PathLike, readFile, unlink, writeFile} from 'fs';
import * as glob from 'glob';
import {Schema as JSONSchema, ValidationError} from 'jsonschema';
import {Definition} from 'ts-json-schema-generator';
import {Application, ProjectReflection} from 'typedoc';
import {promisify} from 'util';
-/**
- * Initialized logger
- */
export const logger = new Logger();
-export const globPromisfied = promisify(glob);
+export const globPromisified = promisify(glob);
export const mkdirPromisified = promisify(mkdir);
-export const readFilePromisifed = promisify(readFile);
+export const readFilePromisified = promisify(readFile);
export const writeFilePromisified = promisify(writeFile);
+export const unlinkPromisified = promisify(unlink);
/**
* A route instance with its relevant meta information
diff --git a/src/pack.ts b/src/pack.ts
new file mode 100644
index 00000000..da4ee655
--- /dev/null
+++ b/src/pack.ts
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2018-2019 StApps
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+import * as del from 'del';
+import {existsSync} from 'fs';
+import {basename, dirname, join} from 'path';
+import {cwd} from 'process';
+import {globPromisified, logger, readFilePromisified, unlinkPromisified, writeFilePromisified} from './common';
+
+const PACK_IDENTIFIER = '/* PACKED BY @openstapps/pack */';
+
+/**
+ * A JavaScript module representation to sort a list of them by dependencies
+ */
+interface JavaScriptModule {
+ /**
+ * Content of the module
+ */
+ content: string;
+
+ /**
+ * List of names of dependencies
+ */
+ dependencies: string[];
+
+ /**
+ * Directory the module is in
+ */
+ directory: string;
+
+ /**
+ * The name of the module
+ */
+ name: string;
+}
+
+/**
+ * Pack cli.js
+ *
+ * This finds all internal requires and replaces the paths with `./index` or internal requires if it has been
+ * required already.
+ *
+ * Furthermore it checks that no shebang line is present and that it does not export anything.
+ */
+async function packCliJs(): Promise {
+ const path = join(cwd(), 'lib', 'cli.js');
+
+ if (!existsSync(path)) {
+ return;
+ }
+
+ logger.info('Adjusting JavaScript CLI...');
+
+ const buffer = await readFilePromisified(path);
+ const content = buffer.toString();
+
+ if (content.indexOf('#!/') === 0) {
+ throw new Error('`cli.js` must not contain a shebang line! It is added by this script.');
+ }
+
+ let internalRequire: string | null = null;
+
+ const adjustedContent = '#!/usr/bin/env node\n\n' + content
+ .split('\n')
+ .map((line, lineNumber) => {
+
+ // check for exports (cli.js is not allowed to export anything)
+ if (Array.isArray(line.match(/^\s*((exports)|(module\.exports))/))) {
+ throw new Error(
+ 'Line ' +
+ lineNumber +
+ ' in cli.js has a reference to the exports object. cli.js is not for exporting. Line was: "'
+ + line
+ + '"',
+ );
+ }
+
+ // replace lines with internal requires
+ // extract module name from line
+ const match = line.match(/^(\s*)(const|var) ([a-z0-9_]*) = require\(("[^"]+"|'[^']+')\);$/i);
+
+ if (match !== null) {
+ const importedName = match[3];
+ const moduleName = match[4].substring(1, match[4].length - 1);
+
+ // if it begins with '.' and not ends with json
+ if (/^[.]{1,2}\/(?!.*\.json$).*$/i.test(moduleName)) {
+
+ // is the first internal require
+ if (internalRequire !== null) {
+ return 'const ' + importedName + ' = ' + internalRequire + ';';
+ }
+
+ // only the first import needs a require
+ internalRequire = importedName;
+ return 'const ' + importedName + ' = require("./index");';
+ }
+ }
+ return line;
+ })
+ .join('\n');
+
+ return await writeFilePromisified(path, adjustedContent);
+}
+
+/**
+ * Get a list containing the contents of all type definition files
+ */
+async function getAllTypeDefinitions(): Promise {
+ const fileNames = await globPromisified(join(cwd(), '*(lib|src)', '**', '*.d.ts'), {
+ ignore: [
+ join(cwd(), 'lib', 'doc', '**', '*.d.ts'),
+ join(cwd(), 'lib', 'test', '**', '*.d.ts'),
+ join(cwd(), 'lib', 'cli.d.ts'),
+ ],
+ });
+
+ const promises = fileNames.map((fileName) => {
+ return readFilePromisified(fileName, 'utf8');
+ });
+
+ return await Promise.all(promises);
+}
+
+/**
+ * Pack a list of type definitions into one file
+ */
+async function packTypeDefinitions(): Promise {
+ logger.info('Packing TypeScript definition files...');
+
+ const path = join(cwd(), 'lib', 'index.d.ts');
+
+ await deleteFileIfExistingAndPacked(path);
+
+ const typeDefinitions = await getAllTypeDefinitions();
+
+ // pack TypeScript definition files
+ const imports: { [k: string]: string[] } = {};
+
+ const referenceLines: string[] = [];
+
+ let allDefinitions = typeDefinitions
+ // concat them separated by new lines
+ .join('\n\n\n\n\n')
+ // split all lines
+ .split('\n')
+ .map((line) => {
+ if (line.indexOf('export =') !== -1) {
+ throw new Error('`export =` is not allowed by pack. Use named imports instead.');
+ }
+
+ if (line.indexOf('/// {
+ return object.trim();
+ });
+
+ // add list of already imported objects for module
+ if (typeof imports[module] === 'undefined') {
+ imports[module] = [];
+ }
+
+ // count already imported objects and objects to import now
+ const objectsToImport: string[] = [];
+ importedObjects.forEach((object) => {
+ if (imports[module].indexOf(object) === -1) {
+ imports[module].push(object);
+ objectsToImport.push(object);
+ }
+ });
+
+ // replace import line
+ if (objectsToImport.length === 0) {
+ return '// extraneous removed import';
+ } else {
+ return 'import { ' + objectsToImport.join(', ') + ' } from \'' + module + '\';';
+ }
+ }
+
+ return line;
+ })
+ // filter lines which contain "local" imports
+ .filter((line) => {
+ return !line.match(/^import .* from '\./);
+ })
+ // concat all lines separated by new lines
+ .join('\n');
+
+ if (allDefinitions.length > 0) {
+ if (referenceLines.length > 0) {
+ allDefinitions = referenceLines.join('\n') + '\n\n' + allDefinitions;
+ }
+
+ // write packed TypeScript definition files
+ return await writeFilePromisified(path, PACK_IDENTIFIER + '\n\n' + allDefinitions);
+ }
+}
+
+/**
+ * Get all JavaScript modules
+ */
+async function getAllJavaScriptModules(): Promise {
+ const fileNames = await globPromisified(join(cwd(), 'lib', '**', '*.js'), {
+ ignore: [
+ join(cwd(), 'lib', 'doc', '**', '*.js'),
+ join(cwd(), 'lib', 'test', '*.js'),
+ join(cwd(), 'lib', 'cli.js'),
+ ],
+ });
+
+ const promises = fileNames.map(async (fileName) => {
+ const fileContent = await readFilePromisified(fileName, 'utf8');
+ const directory = dirname(fileName).replace(new RegExp('^' + join(cwd(), 'lib')), '');
+
+ return {
+ content: '(function() {\n' + fileContent + '\n})();\n',
+ dependencies: getAllInternalDependencies(fileContent),
+ directory: directory,
+ name: basename(fileName, '.js'),
+ };
+ });
+
+ return await Promise.all(promises);
+}
+
+/**
+ * Pack all javascript files
+ */
+async function packJavaScriptFiles(): Promise {
+ const path = join(cwd(), 'lib', 'index.js');
+
+ logger.info('Packing JavaScript files...');
+
+ await deleteFileIfExistingAndPacked(path);
+
+ // topologically sort the modules (sort by dependencies)
+ const jsModules = topologicalSort(await getAllJavaScriptModules());
+
+ let wholeCode = jsModules
+ // convert modules to strings
+ .map((module) => {
+ module.content = module.content
+ .split('\n')
+ .map((line) => {
+ const match = line.match(
+ /^(\s*)(const|var) ([a-z0-9_]*) = ((require\("([^"]+)"\))|(require\('([^']+)'\)));$/i,
+ );
+
+ // replace lines with internal requires
+ if (match !== null) {
+ // match[6] or match[8] contain the modulePath
+ if (typeof match[6] === 'undefined') {
+ match[6] = match[8];
+ }
+
+ const whiteSpace = match[1] ? match[1] : '';
+ const importedName = match[3];
+ const modulePath = match[6];
+
+ // leave line unchanged if it is a "global" import
+ if (modulePath.match(/^[.]{1,2}\//) === null) {
+ return line;
+ }
+
+ // replace internal requires with `module.exports`
+ if (existsSync(join(cwd(), 'lib', module.directory, modulePath + '.js'))) {
+ return whiteSpace + 'const ' + importedName + ' = module.exports;';
+ }
+
+ if (existsSync(join(cwd(), 'src', module.directory, modulePath))) {
+ return whiteSpace + 'const ' + importedName + ' = require(\'../src/' + modulePath + '\');';
+ }
+
+ logger.warn('Import ' + importedName + ' could not be found in module.directory ' + modulePath);
+ }
+
+ return line;
+ })
+ .join('\n');
+
+ return '// Module: ' + module.name + '\n' + module.content;
+ })
+ // concat them separated by new lines
+ .join('\n\n\n\n\n')
+ // split all lines
+ .split('\n')
+ // filter lines
+ .filter((line) => {
+ // remove strict usage
+ if (line === '"use strict";') {
+ return false;
+ }
+
+ // remove esModule property
+ if (line === 'Object.defineProperty(exports, "__esModule", { value: true });') {
+ return false;
+ }
+
+ // remove source map references
+ if (line.indexOf('//# sourceMappingURL=') === 0) {
+ return false;
+ }
+
+ // keep all other lines
+ return true;
+ })
+ // concat all lines separated by new lines
+ .join('\n');
+
+ if (wholeCode.length > 0) {
+ // add meta lines to the file
+ wholeCode = '"use strict";\nObject.defineProperty(exports, "__esModule", { value: true });\n\n' + wholeCode;
+
+ // write packed JavaScript files
+ return await writeFilePromisified(path, PACK_IDENTIFIER + '\n\n' + wholeCode);
+ }
+}
+
+/**
+ * Delete file if it exists and is packed by this script
+ *
+ * @param path Path to file to check/delete
+ */
+async function deleteFileIfExistingAndPacked(path: string): Promise {
+ try {
+ const buffer = await readFilePromisified(path);
+ const content = buffer.toString();
+
+ // check if packed by this script
+ if (content.indexOf(PACK_IDENTIFIER) === 0) {
+ logger.log('Found `' + path + '` which is packed by this script. Deleting it...');
+ return await unlinkPromisified(path);
+ }
+ } catch (err) {
+ if (err.code === 'ENOENT') {
+ return;
+ }
+ }
+}
+
+/**
+ * Get all internal dependencies from the content of a module
+ *
+ * @param moduleContent Module content to analyze
+ */
+function getAllInternalDependencies(moduleContent: string): string[] {
+ // match all const = require();
+ const requireLines =
+ moduleContent.match(/^\s*(const|var) [a-z0-9_]* = require\("([^"]+)"\)|require\('([^']+)'\);$/gmi);
+
+ if (Array.isArray(requireLines)) {
+ return requireLines.map((requireLine) => {
+ const matches = requireLine.match(/require\("([^"]+)"\)|require\('([^']+)'\);$/i);
+
+ // previously matched require line does not contain a require?!
+ if (matches === null) {
+ throw new Error();
+ }
+
+ // return only the moduleName
+ return matches[1];
+ }).filter((moduleName) => {
+ // filter out internal modules beginning with './' and not ending with '.json'
+ return /^[.]{1,2}\/(?!.*\.json$).*$/i.test(moduleName);
+ }).map((internalModuleName) => {
+ // cut './' from the name
+ return internalModuleName.substring(2);
+ });
+ }
+
+ return [];
+}
+
+/**
+ * Sort modules by their dependencies
+ *
+ * @param modules Modules to sort
+ */
+function topologicalSort(modules: JavaScriptModule[]): JavaScriptModule[] {
+ const topoSort = require('toposort');
+
+ // vertices are modules, an edge from a to b means that b depends on a
+ const edges: string[][] = [];
+ const nodes: string[] = [];
+
+ // add all edges
+ modules.forEach((module) => {
+ module.dependencies.forEach((dependenciePath) => {
+ // add edge from dependency to our module
+ edges.push([basename(dependenciePath), module.name]);
+ });
+
+ nodes.push(module.name);
+ });
+
+ // sort graph and return as an array of sorted modules
+ return topoSort.array(nodes, edges).map((moduleName: string) => {
+ return modules.find((module) => {
+ return module.name === moduleName;
+ });
+ });
+}
+
+/**
+ * Pack
+ */
+export async function pack() {
+ logger.log(`Packing project in ${process.cwd()}...`);
+
+ // run all tasks in parallel
+ const promises: Array> = [
+ packCliJs(),
+ packTypeDefinitions(),
+ packJavaScriptFiles(),
+ ];
+
+ await Promise.all(promises);
+
+ // clean up afterwards
+ logger.info('Deleting extraneous files...');
+
+ await del([
+ // delete all transpiled files
+ 'lib/*',
+
+ // keep packed files
+ '!lib/index.d.ts', '!lib/index.js',
+
+ // keep converted schema files
+ '!lib/schema', '!lib/schema/*.json',
+
+ // keep documentation
+ '!lib/doc', '!lib/doc/*', '!lib/doc/**/*',
+
+ // keep cli
+ '!lib/cli.js',
+
+ // keep tests
+ '!lib/test', '!lib/test/*', '!lib/test/**/*',
+ ]);
+}
diff --git a/src/validate.ts b/src/validate.ts
index 86cd047b..9641adce 100644
--- a/src/validate.ts
+++ b/src/validate.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018-2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -19,10 +19,10 @@ import * as mustache from 'mustache';
import {basename, join, resolve} from 'path';
import {
ExpectableValidationErrors,
- globPromisfied,
+ globPromisified,
isThingWithType,
logger,
- readFilePromisifed,
+ readFilePromisified,
writeFilePromisified,
} from './common';
@@ -53,7 +53,7 @@ export class Validator {
* @param schemaDir Path to directory that contains schema files
*/
public async addSchemas(schemaDir: PathLike): Promise {
- const schemaFiles = await globPromisfied(join(schemaDir.toString(), '*.json'));
+ const schemaFiles = await globPromisified(join(schemaDir.toString(), '*.json'));
if (schemaFiles.length === 0) {
throw new Error(`No schema files in ${schemaDir.toString()}!`);
@@ -64,7 +64,7 @@ export class Validator {
// Iterate over schema files
await asyncPool(2, schemaFiles, async (file) => {
// read schema file
- const buffer = await readFilePromisifed(file);
+ const buffer = await readFilePromisified(file);
const schema = JSON.parse(buffer.toString());
// add schema to validator
@@ -124,7 +124,7 @@ export async function validateFiles(schemaDir: string, resourcesDir: string): Pr
await v.addSchemas(schemaDir);
// get list of files to test
- const testFiles = await globPromisfied(join(resourcesDir, '*.json'));
+ const testFiles = await globPromisified(join(resourcesDir, '*.json'));
if (testFiles.length === 0) {
throw new Error(`No test files in ${resourcesDir}!`);
@@ -139,7 +139,7 @@ export async function validateFiles(schemaDir: string, resourcesDir: string): Pr
await asyncPool(2, testFiles, async (testFile) => {
const testFileName = basename(testFile);
- const buffer = await readFilePromisifed(join(resourcesDir, testFileName));
+ const buffer = await readFilePromisified(join(resourcesDir, testFileName));
// read test description from file
const testDescription = JSON.parse(buffer.toString());
@@ -208,10 +208,10 @@ export async function validateFiles(schemaDir: string, resourcesDir: string): Pr
* @param errors Errors that occurred in validation
*/
export async function writeReport(reportPath: PathLike, errors: ExpectableValidationErrors): Promise {
- let buffer = await readFilePromisifed(resolve(__dirname, '..', 'resources', 'file.html.mustache'));
+ let buffer = await readFilePromisified(resolve(__dirname, '..', 'resources', 'file.html.mustache'));
const fileTemplate = buffer.toString();
- buffer = await readFilePromisifed(resolve(__dirname, '..', 'resources', 'error.html.mustache'));
+ buffer = await readFilePromisified(resolve(__dirname, '..', 'resources', 'error.html.mustache'));
const errorTemplate = buffer.toString();
let output = '';
@@ -236,7 +236,7 @@ export async function writeReport(reportPath: PathLike, errors: ExpectableValida
});
});
- buffer = await readFilePromisifed(resolve(__dirname, '..', 'resources', 'report.html.mustache'));
+ buffer = await readFilePromisified(resolve(__dirname, '..', 'resources', 'report.html.mustache'));
const reportTemplate = buffer.toString();
await writeFilePromisified(reportPath, mustache.render(reportTemplate, {