1 | #!/usr/bin/env node
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | 'use strict';
|
19 |
|
20 | const debug = require('debug')('loopback:build');
|
21 | const utils = require('./utils');
|
22 | const path = require('path');
|
23 | const fs = require('fs');
|
24 | const {globSync} = require('glob');
|
25 | const fse = require('fs-extra');
|
26 | const {buildOpts: buildOptions} = require('typescript');
|
27 |
|
28 | function run(argv, options) {
|
29 | if (options === true) {
|
30 | options = {dryRun: true};
|
31 | } else {
|
32 | options = options || {};
|
33 | }
|
34 |
|
35 | const packageDir = utils.getPackageDir();
|
36 |
|
37 | const runnerName = argv[1];
|
38 |
|
39 | const compilerOpts = argv.slice(2);
|
40 | const runnerIsLbttsc = runnerName.includes('lb-ttsc');
|
41 | const isUseTtscSet = utils.isOptionSet(compilerOpts, '--use-ttypescript');
|
42 | const useTtsc = runnerIsLbttsc || isUseTtscSet;
|
43 | const isTargetSet = utils.isOptionSet(compilerOpts, '--target');
|
44 | const isOutDirSet = utils.isOptionSet(compilerOpts, '--outDir');
|
45 | const isProjectSet = utils.isOptionSet(compilerOpts, '-p', '--project');
|
46 | const isCopyResourcesSet = utils.isOptionSet(
|
47 | compilerOpts,
|
48 | '--copy-resources',
|
49 | );
|
50 |
|
51 | let TSC_CLI = 'typescript/lib/tsc';
|
52 | if (useTtsc) {
|
53 | try {
|
54 | require.resolve('ttypescript');
|
55 | TSC_CLI = 'ttypescript/lib/tsc';
|
56 | } catch (e) {
|
57 | if (isUseTtscSet) {
|
58 | console.error(
|
59 | 'Error using the --use-ttypescript option - ttypescript is not installed',
|
60 | );
|
61 | } else {
|
62 | console.error('Error using lb-ttsc - ttypescript is not installed');
|
63 | }
|
64 | process.exit(1);
|
65 | }
|
66 | }
|
67 | debug(`Using ${TSC_CLI} to compile package`);
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | if (isCopyResourcesSet) {
|
73 | compilerOpts.splice(compilerOpts.indexOf('--copy-resources'), 1);
|
74 | }
|
75 |
|
76 | if (isUseTtscSet) {
|
77 | compilerOpts.splice(compilerOpts.indexOf('--use-ttypescript'), 1);
|
78 | }
|
79 |
|
80 | let target;
|
81 | if (isTargetSet) {
|
82 | const targetIx = compilerOpts.indexOf('--target');
|
83 | target = compilerOpts[targetIx + 1];
|
84 | compilerOpts.splice(targetIx, 2);
|
85 | }
|
86 |
|
87 | let outDir;
|
88 | if (isOutDirSet) {
|
89 | const outDirIx = compilerOpts.indexOf('--outDir');
|
90 | outDir = path.resolve(process.cwd(), compilerOpts[outDirIx + 1]);
|
91 | compilerOpts.splice(outDirIx, 2);
|
92 | }
|
93 |
|
94 | let tsConfigFile;
|
95 |
|
96 | let rootDir;
|
97 | if (!isProjectSet) {
|
98 | rootDir = utils.getRootDir();
|
99 | tsConfigFile = utils.getConfigFile('tsconfig.build.json', 'tsconfig.json');
|
100 | if (tsConfigFile === path.join(rootDir, 'config/tsconfig.build.json')) {
|
101 |
|
102 | let baseConfigFile = path.join(rootDir, 'config/tsconfig.common.json');
|
103 | baseConfigFile = path.relative(packageDir, baseConfigFile);
|
104 | if (baseConfigFile.indexOf('..' + path.sep) !== 0) {
|
105 |
|
106 | baseConfigFile = '.' + path.sep + baseConfigFile;
|
107 | }
|
108 | baseConfigFile = baseConfigFile.replace(/\\/g, '/');
|
109 |
|
110 |
|
111 | tsConfigFile = path.join(packageDir, 'tsconfig.json');
|
112 | fs.writeFileSync(
|
113 | tsConfigFile,
|
114 | JSON.stringify(
|
115 | {
|
116 | extends: baseConfigFile,
|
117 | compilerOptions: {
|
118 | outDir: 'dist',
|
119 | rootDir: 'src',
|
120 | },
|
121 | include: ['src'],
|
122 | },
|
123 | null,
|
124 | ' ',
|
125 | ),
|
126 | );
|
127 | }
|
128 | }
|
129 |
|
130 | const args = [];
|
131 |
|
132 | let cwd = process.env.LERNA_ROOT_PATH || process.cwd();
|
133 | if (tsConfigFile && fs.existsSync(tsConfigFile)) {
|
134 | const tsconfig = require(tsConfigFile);
|
135 | if (tsconfig.references) {
|
136 | args.unshift('-b');
|
137 |
|
138 | cwd = process.cwd();
|
139 | } else {
|
140 |
|
141 | args.push('-p', path.relative(cwd, tsConfigFile));
|
142 | }
|
143 | }
|
144 |
|
145 | if (outDir) {
|
146 | args.push('--outDir', path.relative(cwd, outDir));
|
147 | }
|
148 |
|
149 | if (target) {
|
150 | args.push('--target', target);
|
151 | }
|
152 |
|
153 | if (isCopyResourcesSet) {
|
154 |
|
155 |
|
156 |
|
157 | copyResources(rootDir, packageDir, tsConfigFile, outDir, options);
|
158 | }
|
159 |
|
160 | if (target) {
|
161 | args.push('--target', target);
|
162 | }
|
163 |
|
164 | args.push(...compilerOpts);
|
165 |
|
166 | const validArgs = validArgsForBuild(args);
|
167 |
|
168 | return utils.runCLI(TSC_CLI, validArgs, {cwd, ...options});
|
169 | }
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 | function validArgsForBuild(args) {
|
177 | const validBooleanOptions = [];
|
178 | const validValueOptions = [];
|
179 |
|
180 |
|
181 | buildOptions.forEach(opt => {
|
182 | |
183 |
|
184 |
|
185 |
|
186 |
|
187 | const options =
|
188 | opt.type === 'boolean' ? validBooleanOptions : validValueOptions;
|
189 | options.push(`--${opt.name}`);
|
190 | if (opt.shortName) {
|
191 | validBooleanOptions.push(`-${opt.shortName}`);
|
192 | }
|
193 | });
|
194 | let validArgs = args;
|
195 | if (args.includes('-b') || args.includes('--build')) {
|
196 | validArgs = filterArgs(args, (arg, next) => {
|
197 | if (validBooleanOptions.includes(arg)) {
|
198 | return next === 'false' || next === 'true' ? 2 : 1;
|
199 | }
|
200 | if (validValueOptions.includes(arg)) return 2;
|
201 | return 0;
|
202 | });
|
203 |
|
204 | validArgs.unshift('-b');
|
205 | }
|
206 | debug('Valid args for tsc -b', validArgs);
|
207 | return validArgs;
|
208 | }
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 | function filterArgs(args, filter) {
|
216 | const validArgs = [];
|
217 | let i = 0;
|
218 | while (i < args.length) {
|
219 | const length = filter(args[i], args[i + 1]);
|
220 | if (length === 0) {
|
221 | i++;
|
222 | } else if (length === 1) {
|
223 | validArgs.push(args[i]);
|
224 | i++;
|
225 | } else if (length === 2) {
|
226 | validArgs.push(args[i], args[i + 1]);
|
227 | i += 2;
|
228 | }
|
229 | }
|
230 | return validArgs;
|
231 | }
|
232 |
|
233 | module.exports = run;
|
234 | if (require.main === module) run(process.argv);
|
235 |
|
236 | function copyResources(rootDir, packageDir, tsConfigFile, outDir, options) {
|
237 | if (!rootDir) {
|
238 | console.warn('Ignoring --copy-resources option - rootDir was not set.');
|
239 | return;
|
240 | }
|
241 | if (!tsConfigFile) {
|
242 | console.warn(
|
243 | 'Ignoring --copy-resources option - no tsconfig file was found.',
|
244 | );
|
245 | return;
|
246 | }
|
247 |
|
248 | const tsConfig = require(tsConfigFile);
|
249 |
|
250 | if (!outDir) {
|
251 | outDir = tsConfig.compilerOptions && tsConfig.compilerOptions.outDir;
|
252 | if (!outDir) {
|
253 | console.warn(
|
254 | 'Ignoring --copy-resources option - outDir was not configured.',
|
255 | );
|
256 | return;
|
257 | }
|
258 | }
|
259 |
|
260 | const dirs = tsConfig.include
|
261 | ? tsConfig.include.join('|')
|
262 | : ['src', 'test'].join('|');
|
263 |
|
264 | const compilerRootDir =
|
265 | (tsConfig.compilerOptions && tsConfig.compilerOptions.rootDir) || '';
|
266 |
|
267 | const pattern = `@(${dirs})/**/!(*.ts)`;
|
268 | const files = globSync(pattern, {root: packageDir, nodir: true});
|
269 | for (const file of files) {
|
270 | |
271 |
|
272 |
|
273 | let targetFile = file;
|
274 | if (compilerRootDir && file.startsWith(compilerRootDir + '/')) {
|
275 | targetFile = file.substring(compilerRootDir.length + 1);
|
276 | }
|
277 |
|
278 | const copyFrom = path.join(packageDir, file);
|
279 | const copyTo = path.join(outDir, targetFile);
|
280 | debug(' copy %j to %j', copyFrom, copyTo);
|
281 | if (!options.dryRun) {
|
282 | fse.copySync(copyFrom, copyTo);
|
283 | }
|
284 | }
|
285 | }
|