Skip to content

Commit

Permalink
chore: add script to build typescript project references
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Aug 23, 2018
1 parent ec37f2f commit 1e6dd21
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 5 deletions.
111 changes: 111 additions & 0 deletions bin/update-project-refs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env node
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
// Node module: loopback-next
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/**
* This is an internal script to update TypeScript project references based on
* lerna's local package dependencies.
*
* See https://www.typescriptlang.org/docs/handbook/project-references.html
*/
'use strict';

const path = require('path');
const fs = require('fs');
const util = require('util');
const debug = require('debug')('loopback:build');
const buildUtils = require('../packages/build/bin/utils');

const Project = require('@lerna/project');
const PackageGraph = require('@lerna/package-graph');

const TSCONFIG = 'tsconfig.build.json';

async function updateReferences(options) {
options = options || {};
const dryRun = options.dryRun;
const project = new Project(process.cwd());
const packages = await project.getPackages();

const rootRefs = [];
const graph = new PackageGraph(packages);

for (const p of graph.values()) {
debug('Package %s', p.pkg.name);
const pkgLocation = p.pkg.location;
const tsconfigFile = path.join(pkgLocation, TSCONFIG);
// Skip non-typescript packages
if (!fs.existsSync(tsconfigFile)) {
debug('Skipping non-TS package: %s', p.pkg.name);
continue;
}
rootRefs.push({
path: path.join(path.relative(project.rootPath, pkgLocation), TSCONFIG),
});
const tsconfig = require(tsconfigFile);
const refs = [];
for (const d of p.localDependencies.keys()) {
const depPkg = graph.get(d);
// Skip non-typescript packages
if (!fs.existsSync(path.join(depPkg.pkg.location, TSCONFIG))) {
debug('Skipping non-TS dependency: %s', depPkg.pkg.name);
continue;
}
const relativePath = path.relative(pkgLocation, depPkg.pkg.location);
refs.push({path: path.join(relativePath, TSCONFIG)});
}
tsconfig.compilerOptions = tsconfig.compilerOptions || {};
// composite must be true for project refs
tsconfig.compilerOptions.composite = true;
// outDir & target have to be set in tsconfig instead of CLI for tsc -b
tsconfig.compilerOptions.target =
tsconfig.compilerOptions.target || buildUtils.getCompilationTarget();
tsconfig.compilerOptions.outDir =
tsconfig.compilerOptions.outDir ||
buildUtils.getDistribution(tsconfig.compilerOptions.target);
tsconfig.references = refs;

// Convert to JSON
const tsconfigJson = JSON.stringify(tsconfig, null, 2);

if (!dryRun) {
// Using `-f` to overwrite tsconfig.build.json
fs.writeFileSync(tsconfigFile, tsconfigJson + '\n', {encoding: 'utf-8'});
debug('%s has been updated.', tsconfigFile);
} else {
// Otherwise write to console
debug(tsconfigJson);
console.log('%s', p.pkg.name);
refs.forEach(r => console.log(' %s', r.path));
}
}

const rootTsconfigFile = path.join(project.rootPath, 'tsconfig.json');
const rootTsconfig = require(rootTsconfigFile);
rootTsconfig.compilerOptions = rootTsconfig.compilerOptions || {};
rootTsconfig.compilerOptions.composite = true;
rootTsconfig.references = rootRefs;
// Convert to JSON
const rootTsconfigJson = JSON.stringify(rootTsconfig, null, 2);
if (!dryRun) {
// Using `-f` to overwrite tsconfig.json
fs.writeFileSync(rootTsconfigFile, rootTsconfigJson + '\n', {
encoding: 'utf-8',
});
debug('%s has been updated.', rootTsconfigFile);
} else {
debug(rootTsconfigJson);
console.log('\n%s', path.relative(project.rootPath, rootTsconfigFile));
rootRefs.forEach(r => console.log(' %s', r.path));
console.log(
'\nThis is a dry-run. Please use -f option to update tsconfig files.',
);
}
}

if (require.main === module) {
const dryRun = process.argv[2] !== '-f';
updateReferences({dryRun});
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@types/mocha": "^5.0.0",
"coveralls": "^3.0.0",
"cz-conventional-changelog": "^2.1.0",
"debug": "^3.1.0",
"husky": "^0.14.3",
"lerna": "^3.1.4"
},
Expand Down
31 changes: 26 additions & 5 deletions packages/build/bin/compile-package.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ function run(argv, options) {
const glob = require('glob');
const fse = require('fs-extra');

if (options === true) {
options = {dryRun: true};
} else {
options = options || {};
}

const packageDir = utils.getPackageDir();

const compilerOpts = argv.slice(2);
Expand All @@ -36,6 +42,11 @@ function run(argv, options) {
'--ignore-resources',
);

// Honor --dry from tsc
if (utils.isOptionSet(compilerOpts, '--dry')) {
options.dryRun = true;
}

var target;

// --ignore-resources is not a TS Compiler option so we remove it from the
Expand Down Expand Up @@ -126,7 +137,7 @@ function run(argv, options) {
// Since outDir is set, ts files are compiled into that directory.
// If ignore-resources flag is not passed, copy resources (non-ts files)
// to the same outDir as well.
if (rootDir && tsConfigFile && !isIgnoreResourcesSet) {
if (rootDir && tsConfigFile && !isIgnoreResourcesSet && !options.dryRun) {
const tsConfig = require(tsConfigFile);
const dirs = tsConfig.include
? tsConfig.include.join('|')
Expand All @@ -146,11 +157,21 @@ function run(argv, options) {

args.push(...compilerOpts);

if (options === true) {
options = {dryRun: true};
} else {
options = options || {};
// Move --build or -b as the 1st argument to avoid:
// error TS6369: Option '--build' must be the first command line argument.
const buildOptions = utils.removeOptions(args, '-b', '--build');
if (buildOptions.length) {
let projectOptions = utils.removeOptions(args, '-p', '--project');
projectOptions = projectOptions.filter(p => !p.startsWith('-'));
// Remove conflict options with '--build'
utils.removeOptions(args, '--outDir', '--target');
if (buildOptions.length === 1) {
args.unshift(...buildOptions, ...projectOptions);
} else {
args.unshift(...buildOptions);
}
}

return utils.runCLI('typescript/lib/tsc', args, {cwd, ...options});
}

Expand Down
21 changes: 21 additions & 0 deletions packages/build/bin/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,26 @@ function isOptionSet(opts, ...optionNames) {
);
}

/**
* Remove options and their values from args
*/
function removeOptions(args, ...options) {
const removed = [];
for (const e of options) {
const index = args.indexOf(e);
if (index !== -1) {
const next = args[index + 1];
if (typeof next === 'string' && !next.startsWith('-')) {
// The next element is the value of the option, remove it too
removed.push(...args.splice(index, 2));
} else {
removed.push(...args.splice(index, 1));
}
}
}
return removed;
}

exports.getCompilationTarget = getCompilationTarget;
exports.getDistribution = getDistribution;
exports.getRootDir = getRootDir;
Expand All @@ -203,3 +223,4 @@ exports.resolveCLI = resolveCLI;
exports.runCLI = runCLI;
exports.runShell = runShell;
exports.isOptionSet = isOptionSet;
exports.removeOptions = removeOptions;

0 comments on commit 1e6dd21

Please sign in to comment.