microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.1.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

gulpfile.js

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