microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.9.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

gulpfile.js

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