microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.5.2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

gulpfile.js

584lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4const gulp = require("gulp");
5const log = require("fancy-log");
6const sourcemaps = require("gulp-sourcemaps");
7const path = require("path");
8const preprocess = require("gulp-preprocess");
9const ts = require("gulp-typescript");
10const mocha = require("gulp-mocha");
11const GulpExtras = require("./tools/gulp-extras");
12const minimist = require("minimist");
13const os = require("os");
14const fs = require("fs");
15const es = require("event-stream");
16const nls = require("vscode-nls-dev");
17const webpack = require("webpack");
18const TerserPlugin = require("terser-webpack-plugin");
19const filter = require("gulp-filter");
20const del = require("del");
21const vscodeTest = require("vscode-test");
22const cp = require("child_process");
23
24const copyright = GulpExtras.checkCopyright;
25const imports = GulpExtras.checkImports;
26const executeCommand = GulpExtras.executeCommand;
27const tsProject = ts.createProject("tsconfig.json");
28
29/**
30 * Whether we're running a nightly build.
31 */
32const isNightly = process.argv.includes("--nightly");
33
34const vscodeVersionForTests = "1.48.0";
35
36const fullExtensionName = isNightly
37 ? "msjsdiag.vscode-react-native-preview"
38 : "msjsdiag.vscode-react-native";
39
40const extensionName = isNightly ? "vscode-react-native-preview" : "vscode-react-native";
41
42const translationProjectName = "vscode-extensions";
43const defaultLanguages = [
44 { id: "zh-tw", folderName: "cht", transifexId: "zh-hant" },
45 { id: "zh-cn", folderName: "chs", transifexId: "zh-hans" },
46 { id: "ja", folderName: "jpn" },
47 { id: "ko", folderName: "kor" },
48 { id: "de", folderName: "deu" },
49 { id: "fr", folderName: "fra" },
50 { id: "es", folderName: "esn" },
51 { id: "ru", folderName: "rus" },
52 { id: "it", folderName: "ita" },
53
54 // These language-pack languages are included for VS but excluded from the vscode package
55 { id: "cs", folderName: "csy" },
56 { id: "tr", folderName: "trk" },
57 { id: "pt-br", folderName: "ptb", transifexId: "pt-BR" },
58 { id: "pl", folderName: "plk" },
59];
60
61const srcPath = "src";
62const testPath = "test";
63const buildDir = "src";
64const distDir = "dist";
65const distSrcDir = `${distDir}/src`;
66
67const sources = [srcPath, testPath].map(tsFolder => tsFolder + "/**/*.ts");
68
69const knownOptions = {
70 string: "env",
71 default: { env: "production" },
72};
73
74const options = minimist(process.argv.slice(2), knownOptions);
75
76let lintSources = [srcPath, testPath].map(tsFolder => tsFolder + "/**/*.ts");
77lintSources = lintSources.concat([
78 "!src/typings/**",
79 "!test/resources/sampleReactNative022Project/**",
80 "!test/smoke/**",
81 "!/SmokeTestLogs/**",
82]);
83
84async function runWebpack({
85 packages = [],
86 devtool = false,
87 compileInPlace = false,
88 mode = process.argv.includes("watch") ? "development" : "production",
89} = options) {
90 let configs = [];
91 for (const { entry, library, filename } of packages) {
92 const config = {
93 mode,
94 target: "node",
95 entry: path.resolve(entry),
96 output: {
97 path: compileInPlace ? path.resolve(path.dirname(entry)) : path.resolve(distDir),
98 filename: filename || path.basename(entry).replace(".js", ".bundle.js"),
99 devtoolModuleFilenameTemplate: "../[resource-path]",
100 },
101 devtool: devtool,
102 resolve: {
103 extensions: [".js", ".ts", ".json"],
104 },
105 module: {
106 rules: [
107 {
108 test: /\.ts$/,
109 exclude: /node_modules/,
110 use: [
111 {
112 // vscode-nls-dev loader:
113 // * rewrite nls-calls
114 loader: "vscode-nls-dev/lib/webpack-loader",
115 options: {
116 base: path.join(__dirname),
117 },
118 },
119 {
120 // configure TypeScript loader:
121 // * enable sources maps for end-to-end source maps
122 loader: "ts-loader",
123 options: {
124 compilerOptions: {
125 sourceMap: true,
126 },
127 },
128 },
129 ],
130 },
131 ],
132 },
133 optimization: {
134 minimize: true,
135 minimizer: [
136 new TerserPlugin({
137 terserOptions: {
138 format: {
139 comments: /^\**!|@preserve/i,
140 },
141 },
142 extractComments: false,
143 }),
144 ],
145 },
146 node: {
147 __dirname: false,
148 __filename: false,
149 },
150 externals: {
151 vscode: "commonjs vscode",
152 },
153 };
154
155 if (library) {
156 config.output.libraryTarget = "commonjs2";
157 }
158
159 if (process.argv.includes("--analyze-size")) {
160 config.plugins = [
161 new (require("webpack-bundle-analyzer").BundleAnalyzerPlugin)({
162 analyzerMode: "static",
163 reportFilename: path.resolve(distSrcDir, path.basename(entry) + ".html"),
164 }),
165 ];
166 }
167
168 configs.push(config);
169 }
170
171 await new Promise((resolve, reject) =>
172 webpack(configs, (err, stats) => {
173 if (err) {
174 reject(err);
175 } else if (stats.hasErrors()) {
176 reject(stats);
177 } else {
178 resolve();
179 }
180 }),
181 );
182}
183
184// Generates ./dist/nls.bundle.<language_id>.json from files in ./i18n/** *//<src_path>/<filename>.i18n.json
185// Localized strings are read from these files at runtime.
186const generateSrcLocBundle = () => {
187 // Transpile the TS to JS, and let vscode-nls-dev scan the files for calls to localize.
188 return tsProject
189 .src()
190 .pipe(sourcemaps.init())
191 .pipe(tsProject())
192 .js.pipe(nls.createMetaDataFiles())
193 .pipe(nls.createAdditionalLanguageFiles(defaultLanguages, "i18n"))
194 .pipe(nls.bundleMetaDataFiles(fullExtensionName, "dist"))
195 .pipe(nls.bundleLanguageFiles())
196 .pipe(
197 filter([
198 "**/nls.bundle.*.json",
199 "**/nls.metadata.header.json",
200 "**/nls.metadata.json",
201 "!src/**",
202 ]),
203 )
204 .pipe(gulp.dest("dist"));
205};
206
207function build(failOnError, buildNls) {
208 const isProd = options.env === "production";
209 const preprocessorContext = isProd ? { PROD: true } : { DEBUG: true };
210 let gotError = false;
211 log(`Building with preprocessor context: ${JSON.stringify(preprocessorContext)}`);
212 const tsResult = tsProject
213 .src()
214 .pipe(preprocess({ context: preprocessorContext })) //To set environment variables in-line
215 .pipe(sourcemaps.init())
216 .pipe(tsProject());
217
218 return tsResult.js
219 .pipe(buildNls ? nls.rewriteLocalizeCalls() : es.through())
220 .pipe(
221 buildNls
222 ? nls.createAdditionalLanguageFiles(defaultLanguages, "i18n", ".")
223 : es.through(),
224 )
225 .pipe(buildNls ? nls.bundleMetaDataFiles(fullExtensionName, ".") : es.through())
226 .pipe(buildNls ? nls.bundleLanguageFiles() : es.through())
227 .pipe(sourcemaps.write(".", { includeContent: false, sourceRoot: "." }))
228 .pipe(gulp.dest(file => file.cwd))
229 .once("error", () => {
230 gotError = true;
231 })
232 .once("finish", () => {
233 if (failOnError && gotError) {
234 process.exit(1);
235 }
236 });
237}
238
239async function test(inspectCodeCoverage = false) {
240 // Check if arguments were passed
241 if (options.pattern) {
242 log(`\nTesting cases that match pattern: ${options.pattern}`);
243 } else {
244 log(`\nTesting cases that don't match pattern: extensionContext|localizationContext`);
245 }
246
247 try {
248 // The folder containing the Extension Manifest package.json
249 // Passed to `--extensionDevelopmentPath`
250 const extensionDevelopmentPath = __dirname;
251
252 // The path to the extension test runner script
253 // Passed to --extensionTestsPath
254 const extensionTestsPath = path.resolve(__dirname, "test", "index");
255 console.log(extensionTestsPath);
256 // Download VS Code, unzip it and run the integration test
257
258 const testOptions = {
259 extensionDevelopmentPath,
260 extensionTestsPath,
261 version: vscodeVersionForTests,
262 };
263
264 // Activate inspection of code coverage with unit tests
265 if (inspectCodeCoverage) {
266 testOptions.extensionTestsEnv = {
267 COVERAGE: "true",
268 };
269 }
270
271 await vscodeTest.runTests(testOptions);
272 } catch (err) {
273 console.error(err);
274 console.error("Failed to run tests");
275 process.exit(1);
276 }
277}
278
279gulp.task("check-imports", () => {
280 const tsProject = ts.createProject("tsconfig.json");
281 return tsProject.src().pipe(imports());
282});
283
284gulp.task("check-copyright", () => {
285 return gulp
286 .src([
287 "**/*.ts",
288 "**/*.js",
289 "!**/*.d.ts",
290 "!coverage/**",
291 "!node_modules/**",
292 "!test/**/*.js",
293 "!SampleApplication/**",
294 "!test/resources/sampleReactNative022Project/**/*.js",
295 "!test/smoke/package/node_modules/**",
296 "!test/smoke/automation/node_modules/**",
297 "!test/smoke/resources/**",
298 "!test/smoke/vscode/**",
299 "!dist/**",
300 ])
301 .pipe(copyright());
302});
303
304const runPrettier = (onlyStaged, fix, callback) => {
305 const child = cp.fork(
306 "./node_modules/@mixer/parallel-prettier/dist/index.js",
307 [
308 fix ? "--write" : "--list-different",
309 "src/**/*.ts",
310 "test/**/*.ts",
311 "gulpfile.js",
312 "*.md",
313 "!CHANGELOG.md",
314 "!test/smoke/**",
315 "!src/**/*.d.ts",
316 ],
317 {
318 stdio: "inherit",
319 },
320 );
321
322 child.on("exit", code => (code ? callback(`Prettier exited with code ${code}`) : callback()));
323};
324
325const runEslint = (fix, callback) => {
326 let args = ["--color", "src/**/*.ts"];
327 if (fix) {
328 args.push("--fix");
329 }
330 const child = cp.fork("./node_modules/eslint/bin/eslint.js", args, {
331 stdio: "inherit",
332 cwd: __dirname,
333 });
334
335 child.on("exit", code => (code ? callback(`Eslint exited with code ${code}`) : callback()));
336};
337
338gulp.task("format:prettier", callback => runPrettier(false, true, callback));
339gulp.task("format:eslint", callback => runEslint(true, callback));
340gulp.task("format", gulp.series("format:prettier", "format:eslint"));
341
342gulp.task("lint:prettier", callback => runPrettier(false, false, callback));
343gulp.task("lint:eslint", callback => runEslint(false, callback));
344gulp.task("lint", gulp.parallel("lint:prettier", "lint:eslint"));
345
346/** Run webpack to bundle the extension output files */
347gulp.task("webpack-bundle", async () => {
348 const packages = [
349 {
350 entry: `${buildDir}/extension/rn-extension.ts`,
351 filename: "rn-extension.js",
352 library: true,
353 },
354 ];
355 return runWebpack({ packages });
356});
357
358gulp.task("clean", () => {
359 const pathsToDelete = [
360 "src/**/*.js",
361 "src/**/*.js.map",
362 "test/**/*.js",
363 "test/**/*.js.map",
364 "out/",
365 "dist",
366 "!test/resources/sampleReactNative022Project/**/*.js",
367 ".vscode-test/",
368 "nls.*.json",
369 "!test/smoke/**/*",
370 ];
371 return del(pathsToDelete, { force: true });
372});
373
374// TODO: The file property should point to the generated source (this implementation adds an extra folder to the path)
375// We should also make sure that we always generate urls in all the path properties (We shouldn"t have \\s. This seems to
376// be an issue on Windows platforms)
377gulp.task(
378 "build",
379 gulp.series("check-imports", "lint", function runBuild(done) {
380 build(true, true).once("finish", () => {
381 done();
382 });
383 }),
384);
385
386gulp.task(
387 "build-dev",
388 gulp.series("check-imports", "lint", function runBuild(done) {
389 build(false, false).once("finish", () => {
390 done();
391 });
392 }),
393);
394
395gulp.task("quick-build", gulp.series("build-dev"));
396
397gulp.task(
398 "watch",
399 gulp.series("build", function runWatch() {
400 log("Watching build sources...");
401 return gulp.watch(sources, gulp.series("build"));
402 }),
403);
404
405gulp.task("prod-build", gulp.series("clean", "webpack-bundle", generateSrcLocBundle));
406
407gulp.task("default", gulp.series("prod-build"));
408
409gulp.task("test", gulp.series("build", "lint", test));
410
411gulp.task("test-no-build", test);
412
413gulp.task(
414 "test:coverage",
415 gulp.series("quick-build", async function () {
416 await test(true);
417 }),
418);
419
420gulp.task(
421 "watch-build-test",
422 gulp.series("build", "test", function runWatch() {
423 return gulp.watch(sources, gulp.series("build", "test"));
424 }),
425);
426
427gulp.task("package", callback => {
428 const command = path.join(__dirname, "node_modules", ".bin", "vsce");
429 const args = ["package"];
430 executeCommand(command, args, callback);
431});
432
433function readJson(file) {
434 const contents = fs.readFileSync(path.join(__dirname, file), "utf-8").toString();
435 return JSON.parse(contents);
436}
437
438function writeJson(file, jsonObj) {
439 const content = JSON.stringify(jsonObj, null, 2);
440 fs.writeFileSync(path.join(__dirname, file), content);
441}
442
443/**
444 * Generate version number for a nightly build.
445 */
446const getVersionNumber = () => {
447 const date = new Date(new Date().toLocaleString("en-US", { timeZone: "America/Los_Angeles" }));
448
449 return [
450 // YY
451 date.getFullYear(),
452 // MM,
453 date.getMonth() + 1,
454 //DDHH
455 `${date.getDate()}${String(date.getHours()).padStart(2, "0")}`,
456 ].join(".");
457};
458
459gulp.task("release", function prepareLicenses() {
460 const backupFiles = [
461 "LICENSE.txt",
462 "ThirdPartyNotices.txt",
463 "package.json",
464 "package-lock.json",
465 ];
466 const backupFolder = path.resolve(path.join(os.tmpdir(), "vscode-react-native"));
467 if (!fs.existsSync(backupFolder)) {
468 fs.mkdirSync(backupFolder);
469 }
470
471 return Promise.resolve()
472 .then(() => {
473 /* back up LICENSE.txt, ThirdPartyNotices.txt, README.md */
474 log("Backing up license files to " + backupFolder + "...");
475 backupFiles.forEach(fileName => {
476 fs.writeFileSync(path.join(backupFolder, fileName), fs.readFileSync(fileName));
477 });
478
479 /* copy over the release package license files */
480 log("Preparing license files for release...");
481 fs.writeFileSync("LICENSE.txt", fs.readFileSync("release/LICENSE.txt"));
482 fs.writeFileSync(
483 "ThirdPartyNotices.txt",
484 fs.readFileSync("release/ThirdPartyNotices.txt"),
485 );
486 })
487 .then(() => {
488 let packageJson = readJson("package.json");
489 packageJson.main = "./dist/rn-extension";
490 if (isNightly) {
491 log("Performing nightly release...");
492 packageJson.version = getVersionNumber();
493 packageJson.name = extensionName;
494 packageJson.preview = true;
495 packageJson.displayName += " (Preview)";
496 }
497 writeJson("package.json", packageJson);
498 log("Creating release package...");
499 return new Promise((resolve, reject) => {
500 // NOTE: vsce must see npm 3.X otherwise it will not correctly strip out dev dependencies.
501 executeCommand(
502 "vsce",
503 ["package"],
504 arg => {
505 if (arg) {
506 reject(arg);
507 }
508 resolve();
509 },
510 { cwd: path.resolve(__dirname) },
511 );
512 });
513 })
514 .finally(() => {
515 /* restore backed up files */
516 log("Restoring modified files...");
517 backupFiles.forEach(fileName => {
518 fs.writeFileSync(
519 path.join(__dirname, fileName),
520 fs.readFileSync(path.join(backupFolder, fileName)),
521 );
522 });
523 });
524});
525
526// Creates package.i18n.json files for all languages from {workspaceRoot}/i18n folder into project root
527gulp.task("add-i18n", () => {
528 return gulp
529 .src(["package.nls.json"])
530 .pipe(nls.createAdditionalLanguageFiles(defaultLanguages, "i18n"))
531 .pipe(gulp.dest("."));
532});
533
534// Creates MLCP readable .xliff file and saves it locally
535gulp.task(
536 "translations-export",
537 gulp.series("build", function runTranslationExport() {
538 return gulp
539 .src(["package.nls.json", "nls.metadata.header.json", "nls.metadata.json"])
540 .pipe(nls.createXlfFiles(translationProjectName, fullExtensionName))
541 .pipe(gulp.dest(path.join("..", `${translationProjectName}-localization-export`)));
542 }),
543);
544
545// Imports localization from raw localized MLCP strings to VS Code .i18n.json files
546gulp.task(
547 "translations-import",
548 gulp.series(done => {
549 var options = minimist(process.argv.slice(2), {
550 string: "location",
551 default: {
552 location: "../vscode-translations-import",
553 },
554 });
555 es.merge(
556 defaultLanguages.map(language => {
557 let id = language.transifexId || language.id;
558 log(
559 path.join(
560 options.location,
561 id,
562 "vscode-extensions",
563 `${fullExtensionName}.xlf`,
564 ),
565 );
566 return gulp
567 .src(
568 path.join(
569 options.location,
570 id,
571 "vscode-extensions",
572 `${fullExtensionName}.xlf`,
573 ),
574 )
575 .pipe(nls.prepareJsonFiles())
576 .pipe(gulp.dest(path.join("./i18n", language.folderName)));
577 }),
578 ).pipe(
579 es.wait(() => {
580 done();
581 }),
582 );
583 }, "add-i18n"),
584);
585