mirror of https://github.com/microsoft/vscode.git
esm - introduce VSCODE_BUILD_ESM variable (#225586)
This commit is contained in:
parent
7f4969a41e
commit
0dea4804f4
|
@ -30,7 +30,15 @@
|
|||
**/src/vs/*/**/*.d.ts
|
||||
**/src/vs/base/test/common/filters.perf.data.js
|
||||
**/src/vs/loader.js
|
||||
**/src2/**/dompurify.js
|
||||
**/src2/**/marked.js
|
||||
**/src2/**/semver.js
|
||||
**/src2/typings/**/*.d.ts
|
||||
**/src2/vs/*/**/*.d.ts
|
||||
**/src2/vs/base/test/common/filters.perf.data.js
|
||||
**/src2/vs/loader.js
|
||||
**/test/unit/assert.js
|
||||
**/test/unit/assert-esm.js
|
||||
**/test/automation/out/**
|
||||
**/typings/**
|
||||
!.vscode
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"src/vs/base/test/common/filters.perf.data.js": true,
|
||||
"src/vs/base/test/node/uri.perf.data.txt": true,
|
||||
"src/vs/workbench/api/test/browser/extHostDocumentData.test.perf-data.ts": true,
|
||||
"src/vs/base/test/node/uri.test.data.txt": true,
|
||||
"src/vs/editor/test/node/diffing/fixtures/**": true,
|
||||
},
|
||||
"files.readonlyInclude": {
|
||||
|
@ -50,6 +51,7 @@
|
|||
"test/smoke/out/**": true,
|
||||
"test/automation/out/**": true,
|
||||
"test/integration/browser/out/**": true,
|
||||
"src2/**": true,
|
||||
},
|
||||
"files.readonlyExclude": {
|
||||
"build/builtin/*.js": true,
|
||||
|
|
|
@ -36,5 +36,23 @@
|
|||
"private readonly _onDid$1 = new Emitter<$2>();",
|
||||
"readonly onDid$1: Event<$2> = this._onDid$1.event;"
|
||||
],
|
||||
},
|
||||
"esm-comment": {
|
||||
"scope": "typescript,javascript",
|
||||
"prefix": "esm-comment",
|
||||
"body": [
|
||||
"// ESM-comment-begin",
|
||||
"$SELECTION$0",
|
||||
"// ESM-comment-end",
|
||||
]
|
||||
},
|
||||
"esm-uncomment": {
|
||||
"scope": "typescript,javascript",
|
||||
"prefix": "esm-uncomment",
|
||||
"body": [
|
||||
"// ESM-uncomment-begin",
|
||||
"// $SELECTION$0",
|
||||
"// ESM-uncomment-end",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,10 @@ parameters:
|
|||
displayName: "Skip tests"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: VSCODE_BUILD_ESM # TODO@bpasero remove me once ESM is shipped
|
||||
displayName: "️❗ Build as ESM (!FOR TESTING ONLY!) ️❗"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
variables:
|
||||
- name: VSCODE_PRIVATE_BUILD
|
||||
|
@ -110,6 +114,8 @@ variables:
|
|||
value: ${{ parameters.CARGO_REGISTRY }}
|
||||
- name: VSCODE_QUALITY
|
||||
value: ${{ parameters.VSCODE_QUALITY }}
|
||||
- name: VSCODE_BUILD_ESM
|
||||
value: ${{ parameters.VSCODE_BUILD_ESM }}
|
||||
- name: VSCODE_BUILD_STAGE_WINDOWS
|
||||
value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}
|
||||
- name: VSCODE_BUILD_STAGE_LINUX
|
||||
|
@ -217,6 +223,7 @@ extends:
|
|||
- template: build/azure-pipelines/product-compile.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_ESM: ${{ variables.VSCODE_BUILD_ESM }}
|
||||
|
||||
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- stage: CompileCLI
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
parameters:
|
||||
- name: VSCODE_QUALITY
|
||||
type: string
|
||||
- name: VSCODE_BUILD_ESM
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
|
@ -98,6 +101,10 @@ steps:
|
|||
- script: node build/azure-pipelines/distro/mixin-quality
|
||||
displayName: Mixin distro quality
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_ESM, true) }}:
|
||||
- script: node migrate.mjs --disable-watch
|
||||
displayName: Migrate to ESM
|
||||
|
||||
- template: common/install-builtin-extensions.yml@self
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}:
|
||||
|
|
|
@ -9,6 +9,7 @@ const fs = require("fs");
|
|||
const minimatch = require("minimatch");
|
||||
const vscode_universal_bundler_1 = require("vscode-universal-bundler");
|
||||
const cross_spawn_promise_1 = require("@malept/cross-spawn-promise");
|
||||
const esm_1 = require("../lib/esm");
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
async function main(buildDir) {
|
||||
const arch = process.env['VSCODE_ARCH'];
|
||||
|
@ -26,13 +27,14 @@ async function main(buildDir) {
|
|||
'**/CodeResources',
|
||||
'**/Credits.rtf',
|
||||
];
|
||||
const canAsar = !(0, esm_1.isESM)('ASAR disabled in universal build'); // TODO@esm ASAR disabled in ESM
|
||||
await (0, vscode_universal_bundler_1.makeUniversalApp)({
|
||||
x64AppPath,
|
||||
arm64AppPath,
|
||||
asarPath: asarRelativePath,
|
||||
asarPath: canAsar ? asarRelativePath : undefined,
|
||||
outAppPath,
|
||||
force: true,
|
||||
mergeASARs: true,
|
||||
mergeASARs: canAsar,
|
||||
x64ArchFiles: '*/kerberos.node',
|
||||
filesToSkipComparison: (file) => {
|
||||
for (const expected of filesToSkip) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as fs from 'fs';
|
|||
import * as minimatch from 'minimatch';
|
||||
import { makeUniversalApp } from 'vscode-universal-bundler';
|
||||
import { spawn } from '@malept/cross-spawn-promise';
|
||||
import { isESM } from '../lib/esm';
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
|
||||
|
@ -31,13 +32,15 @@ async function main(buildDir?: string) {
|
|||
'**/Credits.rtf',
|
||||
];
|
||||
|
||||
const canAsar = !isESM('ASAR disabled in universal build'); // TODO@esm ASAR disabled in ESM
|
||||
|
||||
await makeUniversalApp({
|
||||
x64AppPath,
|
||||
arm64AppPath,
|
||||
asarPath: asarRelativePath,
|
||||
asarPath: canAsar ? asarRelativePath : undefined,
|
||||
outAppPath,
|
||||
force: true,
|
||||
mergeASARs: true,
|
||||
mergeASARs: canAsar,
|
||||
x64ArchFiles: '*/kerberos.node',
|
||||
filesToSkipComparison: (file: string) => {
|
||||
for (const expected of filesToSkip) {
|
||||
|
|
|
@ -78,6 +78,7 @@ module.exports.indentationFilter = [
|
|||
'!src/vs/base/node/terminateProcess.sh',
|
||||
'!src/vs/base/node/cpuUsage.sh',
|
||||
'!test/unit/assert.js',
|
||||
'!test/unit/assert-esm.js',
|
||||
'!resources/linux/snap/electron-launch',
|
||||
'!build/ext.js',
|
||||
'!build/npm/gyp/patches/gyp_spectre_mitigation_support.patch',
|
||||
|
|
|
@ -9,10 +9,13 @@
|
|||
const gulp = require('gulp');
|
||||
const util = require('./lib/util');
|
||||
const date = require('./lib/date');
|
||||
const esm = require('./lib/esm');
|
||||
const task = require('./lib/task');
|
||||
const compilation = require('./lib/compilation');
|
||||
const optimize = require('./lib/optimize');
|
||||
|
||||
const isESMBuild = typeof process.env.VSCODE_BUILD_ESM === 'string' && process.env.VSCODE_BUILD_ESM.toLowerCase() === 'true';
|
||||
|
||||
/**
|
||||
* @param {boolean} disableMangle
|
||||
*/
|
||||
|
@ -21,8 +24,9 @@ function makeCompileBuildTask(disableMangle) {
|
|||
util.rimraf('out-build'),
|
||||
util.buildWebNodePaths('out-build'),
|
||||
date.writeISODate('out-build'),
|
||||
esm.setESM(isESMBuild),
|
||||
compilation.compileApiProposalNamesTask,
|
||||
compilation.compileTask('src', 'out-build', true, { disableMangle }),
|
||||
compilation.compileTask(isESMBuild ? 'src2' : 'src', 'out-build', true, { disableMangle }),
|
||||
optimize.optimizeLoaderTask('out-build', 'out-build', true)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('
|
|||
const { vscodeWebEntryPoints, vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web');
|
||||
const cp = require('child_process');
|
||||
const log = require('fancy-log');
|
||||
const { isESM } = require('./lib/esm');
|
||||
|
||||
const REPO_ROOT = path.dirname(__dirname);
|
||||
const commit = getVersion(REPO_ROOT);
|
||||
|
@ -299,7 +300,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa
|
|||
|
||||
let packageJsonContents;
|
||||
const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' })
|
||||
.pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined }))
|
||||
.pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined, ...(isESM(`Setting 'type: module' in top level package.json`) ? { type: 'module' } : {}) })) // TODO@esm this should be configured in the top level package.json
|
||||
.pipe(es.through(function (file) {
|
||||
packageJsonContents = file.contents.toString();
|
||||
this.emit('data', file);
|
||||
|
|
|
@ -33,6 +33,7 @@ const minimist = require('minimist');
|
|||
const { compileBuildTask } = require('./gulpfile.compile');
|
||||
const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions');
|
||||
const { promisify } = require('util');
|
||||
const { isESM } = require('./lib/esm');
|
||||
const glob = promisify(require('glob'));
|
||||
const rcedit = promisify(require('rcedit'));
|
||||
|
||||
|
@ -79,10 +80,11 @@ const vscodeResources = [
|
|||
// Do not change the order of these files! They will
|
||||
// be inlined into the target window file in this order
|
||||
// and they depend on each other in this way.
|
||||
const windowBootstrapFiles = [
|
||||
'out-build/vs/loader.js',
|
||||
'out-build/bootstrap-window.js'
|
||||
];
|
||||
const windowBootstrapFiles = [];
|
||||
if (!isESM('Skipping loader.js in window bootstrap files')) {
|
||||
windowBootstrapFiles.push('out-build/vs/loader.js');
|
||||
}
|
||||
windowBootstrapFiles.push('out-build/bootstrap-window.js');
|
||||
|
||||
const commonJSEntryPoints = [
|
||||
'out-build/main.js',
|
||||
|
@ -242,7 +244,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
|||
}
|
||||
|
||||
const name = product.nameShort;
|
||||
const packageJsonUpdates = { name, version };
|
||||
const packageJsonUpdates = { name, version, ...(isESM(`Setting 'type: module' and 'main: out/main.js' in top level package.json`) ? { type: 'module', main: 'out/main.js' } : {}) }; // TODO@esm this should be configured in the top level package.json
|
||||
|
||||
// for linux url handling
|
||||
if (platform === 'linux') {
|
||||
|
@ -275,16 +277,18 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
|||
const jsFilter = util.filter(data => !data.isDirectory() && /\.js$/.test(data.path));
|
||||
const root = path.resolve(path.join(__dirname, '..'));
|
||||
const productionDependencies = getProductionDependencies(root);
|
||||
const dependenciesSrc = productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat();
|
||||
const dependenciesSrc = productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`, `!**/*.mk`]).flat();
|
||||
|
||||
const deps = gulp.src(dependenciesSrc, { base: '.', dot: true })
|
||||
let deps = gulp.src(dependenciesSrc, { base: '.', dot: true })
|
||||
.pipe(filter(['**', `!**/${config.version}/**`, '!**/bin/darwin-arm64-87/**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.js.map']))
|
||||
.pipe(util.cleanNodeModules(path.join(__dirname, '.moduleignore')))
|
||||
.pipe(util.cleanNodeModules(path.join(__dirname, `.moduleignore.${process.platform}`)))
|
||||
.pipe(jsFilter)
|
||||
.pipe(util.rewriteSourceMappingURL(sourceMappingURLBase))
|
||||
.pipe(jsFilter.restore)
|
||||
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), [
|
||||
.pipe(jsFilter.restore);
|
||||
|
||||
if (!isESM('ASAR disabled in VS Code builds')) { // TODO@esm: ASAR disabled in ESM
|
||||
deps = deps.pipe(createAsar(path.join(process.cwd(), 'node_modules'), [
|
||||
'**/*.node',
|
||||
'**/@vscode/ripgrep/bin/*',
|
||||
'**/node-pty/build/Release/*',
|
||||
|
@ -296,6 +300,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
|||
], [
|
||||
'**/*.mk',
|
||||
], 'node_modules.asar'));
|
||||
}
|
||||
|
||||
let all = es.merge(
|
||||
packageJsonStream,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.bundle = bundle;
|
||||
exports.removeDuplicateTSBoilerplate = removeDuplicateTSBoilerplate;
|
||||
exports.removeAllTSBoilerplate = removeAllTSBoilerplate;
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const vm = require("vm");
|
||||
|
@ -226,20 +226,24 @@ function removeAllDuplicateTSBoilerplate(destFiles) {
|
|||
});
|
||||
return destFiles;
|
||||
}
|
||||
function removeAllTSBoilerplate(source) {
|
||||
const seen = new Array(BOILERPLATE.length).fill(true, 0, 10);
|
||||
return removeDuplicateTSBoilerplate(source, seen);
|
||||
}
|
||||
// Taken from typescript compiler => emitFiles
|
||||
const BOILERPLATE = [
|
||||
{ start: /^var __extends/, end: /^}\)\(\);$/ },
|
||||
{ start: /^var __assign/, end: /^};$/ },
|
||||
{ start: /^var __decorate/, end: /^};$/ },
|
||||
{ start: /^var __metadata/, end: /^};$/ },
|
||||
{ start: /^var __param/, end: /^};$/ },
|
||||
{ start: /^var __awaiter/, end: /^};$/ },
|
||||
{ start: /^var __generator/, end: /^};$/ },
|
||||
{ start: /^var __createBinding/, end: /^}\)\);$/ },
|
||||
{ start: /^var __setModuleDefault/, end: /^}\);$/ },
|
||||
{ start: /^var __importStar/, end: /^};$/ },
|
||||
];
|
||||
function removeDuplicateTSBoilerplate(source, SEEN_BOILERPLATE = []) {
|
||||
// Taken from typescript compiler => emitFiles
|
||||
const BOILERPLATE = [
|
||||
{ start: /^var __extends/, end: /^}\)\(\);$/ },
|
||||
{ start: /^var __assign/, end: /^};$/ },
|
||||
{ start: /^var __decorate/, end: /^};$/ },
|
||||
{ start: /^var __metadata/, end: /^};$/ },
|
||||
{ start: /^var __param/, end: /^};$/ },
|
||||
{ start: /^var __awaiter/, end: /^};$/ },
|
||||
{ start: /^var __generator/, end: /^};$/ },
|
||||
{ start: /^var __createBinding/, end: /^}\)\);$/ },
|
||||
{ start: /^var __setModuleDefault/, end: /^}\);$/ },
|
||||
{ start: /^var __importStar/, end: /^};$/ },
|
||||
];
|
||||
const lines = source.split(/\r\n|\n|\r/);
|
||||
const newLines = [];
|
||||
let IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE;
|
||||
|
|
|
@ -54,6 +54,7 @@ export interface IEntryPoint {
|
|||
prepend?: IExtraFile[];
|
||||
append?: IExtraFile[];
|
||||
dest?: string;
|
||||
target?: 'amd' | 'esm';
|
||||
}
|
||||
|
||||
interface IEntryPointMap {
|
||||
|
@ -361,22 +362,26 @@ function removeAllDuplicateTSBoilerplate(destFiles: IConcatFile[]): IConcatFile[
|
|||
return destFiles;
|
||||
}
|
||||
|
||||
export function removeDuplicateTSBoilerplate(source: string, SEEN_BOILERPLATE: boolean[] = []): string {
|
||||
export function removeAllTSBoilerplate(source: string) {
|
||||
const seen = new Array<boolean>(BOILERPLATE.length).fill(true, 0, 10);
|
||||
return removeDuplicateTSBoilerplate(source, seen);
|
||||
}
|
||||
|
||||
// Taken from typescript compiler => emitFiles
|
||||
const BOILERPLATE = [
|
||||
{ start: /^var __extends/, end: /^}\)\(\);$/ },
|
||||
{ start: /^var __assign/, end: /^};$/ },
|
||||
{ start: /^var __decorate/, end: /^};$/ },
|
||||
{ start: /^var __metadata/, end: /^};$/ },
|
||||
{ start: /^var __param/, end: /^};$/ },
|
||||
{ start: /^var __awaiter/, end: /^};$/ },
|
||||
{ start: /^var __generator/, end: /^};$/ },
|
||||
{ start: /^var __createBinding/, end: /^}\)\);$/ },
|
||||
{ start: /^var __setModuleDefault/, end: /^}\);$/ },
|
||||
{ start: /^var __importStar/, end: /^};$/ },
|
||||
];
|
||||
// Taken from typescript compiler => emitFiles
|
||||
const BOILERPLATE = [
|
||||
{ start: /^var __extends/, end: /^}\)\(\);$/ },
|
||||
{ start: /^var __assign/, end: /^};$/ },
|
||||
{ start: /^var __decorate/, end: /^};$/ },
|
||||
{ start: /^var __metadata/, end: /^};$/ },
|
||||
{ start: /^var __param/, end: /^};$/ },
|
||||
{ start: /^var __awaiter/, end: /^};$/ },
|
||||
{ start: /^var __generator/, end: /^};$/ },
|
||||
{ start: /^var __createBinding/, end: /^}\)\);$/ },
|
||||
{ start: /^var __setModuleDefault/, end: /^}\);$/ },
|
||||
{ start: /^var __importStar/, end: /^};$/ },
|
||||
];
|
||||
|
||||
function removeDuplicateTSBoilerplate(source: string, SEEN_BOILERPLATE: boolean[] = []): string {
|
||||
const lines = source.split(/\r\n|\n|\r/);
|
||||
const newLines: string[] = [];
|
||||
let IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE: RegExp;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setESM = setESM;
|
||||
exports.isESM = isESM;
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
// TODO@esm remove this
|
||||
const outDirectory = path.join(__dirname, '..', '..', 'out-build');
|
||||
const esmMarkerFile = path.join(outDirectory, 'esm');
|
||||
function setESM(enabled) {
|
||||
const result = () => new Promise((resolve, _) => {
|
||||
if (enabled) {
|
||||
fs.mkdirSync(outDirectory, { recursive: true });
|
||||
fs.writeFileSync(esmMarkerFile, 'true', 'utf8');
|
||||
console.warn(`Setting build to ESM: true`);
|
||||
}
|
||||
else {
|
||||
console.warn(`Setting build to ESM: false`);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
result.taskName = 'set-esm';
|
||||
return result;
|
||||
}
|
||||
function isESM(logWarning) {
|
||||
try {
|
||||
const res = fs.readFileSync(esmMarkerFile, 'utf8') === 'true';
|
||||
if (res && logWarning) {
|
||||
console.warn(`ESM: ${logWarning}`);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=esm.js.map
|
|
@ -0,0 +1,40 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
// TODO@esm remove this
|
||||
|
||||
const outDirectory = path.join(__dirname, '..', '..', 'out-build');
|
||||
const esmMarkerFile = path.join(outDirectory, 'esm');
|
||||
|
||||
export function setESM(enabled: boolean) {
|
||||
const result = () => new Promise<void>((resolve, _) => {
|
||||
if (enabled) {
|
||||
fs.mkdirSync(outDirectory, { recursive: true });
|
||||
fs.writeFileSync(esmMarkerFile, 'true', 'utf8');
|
||||
console.warn(`Setting build to ESM: true`);
|
||||
} else {
|
||||
console.warn(`Setting build to ESM: false`);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
result.taskName = 'set-esm';
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isESM(logWarning?: string): boolean {
|
||||
try {
|
||||
const res = fs.readFileSync(esmMarkerFile, 'utf8') === 'true';
|
||||
if (res && logWarning) {
|
||||
console.warn(`ESM: ${logWarning}`);
|
||||
}
|
||||
return res;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ const File = require("vinyl");
|
|||
const sm = require("source-map");
|
||||
const path = require("path");
|
||||
const sort = require("gulp-sort");
|
||||
const esm_1 = require("./esm");
|
||||
var CollectStepResult;
|
||||
(function (CollectStepResult) {
|
||||
CollectStepResult[CollectStepResult["Yes"] = 0] = "Yes";
|
||||
|
@ -169,13 +170,23 @@ var _nls;
|
|||
.filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration)
|
||||
.map(n => n)
|
||||
.filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference)
|
||||
.filter(d => d.moduleReference.expression.getText() === '\'vs/nls\'');
|
||||
.filter(d => {
|
||||
if ((0, esm_1.isESM)()) {
|
||||
return d.moduleReference.expression.getText().endsWith(`/nls.js'`);
|
||||
}
|
||||
return d.moduleReference.expression.getText() === '\'vs/nls\'';
|
||||
});
|
||||
// import ... from 'vs/nls';
|
||||
const importDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportDeclaration)
|
||||
.map(n => n)
|
||||
.filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'')
|
||||
.filter(d => {
|
||||
if ((0, esm_1.isESM)()) {
|
||||
return d.moduleSpecifier.getText().endsWith(`/nls.js'`);
|
||||
}
|
||||
return d.moduleSpecifier.getText() === '\'vs/nls\'';
|
||||
})
|
||||
.filter(d => !!d.importClause && !!d.importClause.namedBindings);
|
||||
// `nls.localize(...)` calls
|
||||
const nlsLocalizeCallExpressions = importDeclarations
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as File from 'vinyl';
|
|||
import * as sm from 'source-map';
|
||||
import * as path from 'path';
|
||||
import * as sort from 'gulp-sort';
|
||||
import { isESM } from './esm';
|
||||
|
||||
declare class FileSourceMap extends File {
|
||||
public sourceMap: sm.RawSourceMap;
|
||||
|
@ -231,14 +232,24 @@ module _nls {
|
|||
.filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration)
|
||||
.map(n => <ts.ImportEqualsDeclaration>n)
|
||||
.filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference)
|
||||
.filter(d => (<ts.ExternalModuleReference>d.moduleReference).expression.getText() === '\'vs/nls\'');
|
||||
.filter(d => {
|
||||
if (isESM()) {
|
||||
return (<ts.ExternalModuleReference>d.moduleReference).expression.getText().endsWith(`/nls.js'`);
|
||||
}
|
||||
return (<ts.ExternalModuleReference>d.moduleReference).expression.getText() === '\'vs/nls\'';
|
||||
});
|
||||
|
||||
// import ... from 'vs/nls';
|
||||
const importDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportDeclaration)
|
||||
.map(n => <ts.ImportDeclaration>n)
|
||||
.filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'')
|
||||
.filter(d => {
|
||||
if (isESM()) {
|
||||
return d.moduleSpecifier.getText().endsWith(`/nls.js'`);
|
||||
}
|
||||
return d.moduleSpecifier.getText() === '\'vs/nls\'';
|
||||
})
|
||||
.filter(d => !!d.importClause && !!d.importClause.namedBindings);
|
||||
|
||||
// `nls.localize(...)` calls
|
||||
|
|
|
@ -22,6 +22,7 @@ const i18n_1 = require("./i18n");
|
|||
const stats_1 = require("./stats");
|
||||
const util = require("./util");
|
||||
const postcss_1 = require("./postcss");
|
||||
const esm_1 = require("./esm");
|
||||
const REPO_ROOT_PATH = path.join(__dirname, '../..');
|
||||
function log(prefix, message) {
|
||||
fancyLog(ansiColors.cyan('[' + prefix + ']'), message);
|
||||
|
@ -148,7 +149,7 @@ const DEFAULT_FILE_HEADER = [
|
|||
].join('\n');
|
||||
function optimizeAMDTask(opts) {
|
||||
const src = opts.src;
|
||||
const entryPoints = opts.entryPoints;
|
||||
const entryPoints = opts.entryPoints.filter(d => d.target !== 'esm');
|
||||
const resources = opts.resources;
|
||||
const loaderConfig = opts.loaderConfig;
|
||||
const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER;
|
||||
|
@ -194,6 +195,162 @@ function optimizeAMDTask(opts) {
|
|||
languages: opts.languages
|
||||
}) : es.through());
|
||||
}
|
||||
function optimizeESMTask(opts, cjsOpts) {
|
||||
// TODO@esm honor IEntryPoint#prepred/append (unused?)
|
||||
const esbuild = require('esbuild');
|
||||
const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER;
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const resourcesStream = es.through(); // this stream will contain the resources
|
||||
const bundlesStream = es.through(); // this stream will contain the bundled files
|
||||
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
|
||||
const entryPoints = opts.entryPoints.filter(d => d.target !== 'amd');
|
||||
if (cjsOpts) {
|
||||
cjsOpts.entryPoints.forEach(entryPoint => entryPoints.push({ name: path.parse(entryPoint).name }));
|
||||
}
|
||||
// TODO@esm remove hardcoded entry point and support `dest` of `IEntryPoint` or clean that up
|
||||
entryPoints.push({ name: 'vs/base/worker/workerMain' });
|
||||
const allMentionedModules = new Set();
|
||||
for (const entryPoint of entryPoints) {
|
||||
allMentionedModules.add(entryPoint.name);
|
||||
entryPoint.include?.forEach(allMentionedModules.add, allMentionedModules);
|
||||
entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules);
|
||||
}
|
||||
// TODO@esm remove this from the bundle files
|
||||
allMentionedModules.delete('vs/css');
|
||||
const bundleAsync = async () => {
|
||||
let bundleData;
|
||||
const files = [];
|
||||
const tasks = [];
|
||||
for (const entryPoint of entryPoints) {
|
||||
const t1 = performance.now();
|
||||
console.log(`[bundle] STARTING '${entryPoint.name}'...`);
|
||||
// support for 'dest' via esbuild#in/out
|
||||
const dest = entryPoint.dest?.replace(/\.[^/.]+$/, '') ?? entryPoint.name;
|
||||
// const dest = entryPoint.name;
|
||||
// boilerplate massage
|
||||
const banner = { js: '' };
|
||||
const fs = await Promise.resolve().then(() => require('node:fs'));
|
||||
const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js');
|
||||
banner.js += await fs.promises.readFile(tslibPath, 'utf-8');
|
||||
const boilerplateTrimmer = {
|
||||
name: 'boilerplate-trimmer',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.js$/ }, async (args) => {
|
||||
const contents = await fs.promises.readFile(args.path, 'utf-8');
|
||||
const newContents = bundle.removeAllTSBoilerplate(contents);
|
||||
return { contents: newContents };
|
||||
});
|
||||
}
|
||||
};
|
||||
// support for 'preprend' via the esbuild#banner
|
||||
if (entryPoint.prepend?.length) {
|
||||
for (const item of entryPoint.prepend) {
|
||||
const fullpath = path.join(REPO_ROOT_PATH, opts.src, item.path);
|
||||
const source = await fs.promises.readFile(fullpath, 'utf8');
|
||||
banner.js += source + '\n';
|
||||
}
|
||||
}
|
||||
const task = esbuild.build({
|
||||
logLevel: 'silent',
|
||||
bundle: true,
|
||||
external: entryPoint.exclude,
|
||||
packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages
|
||||
platform: 'neutral', // makes esm
|
||||
format: 'esm',
|
||||
plugins: [boilerplateTrimmer],
|
||||
target: ['es2023'],
|
||||
loader: {
|
||||
'.ttf': 'file',
|
||||
'.svg': 'file',
|
||||
'.png': 'file',
|
||||
'.sh': 'file',
|
||||
},
|
||||
banner,
|
||||
entryPoints: [
|
||||
{
|
||||
in: path.join(REPO_ROOT_PATH, opts.src, `${entryPoint.name}.js`),
|
||||
out: dest,
|
||||
}
|
||||
],
|
||||
outdir: path.join(REPO_ROOT_PATH, opts.src),
|
||||
write: false, // enables res.outputFiles
|
||||
metafile: true, // enables res.metafile
|
||||
}).then(res => {
|
||||
console.log(`[bundle] DONE for '${entryPoint.name}' (${Math.round(performance.now() - t1)}ms)`);
|
||||
if (opts.bundleInfo) {
|
||||
// TODO@esm validate that bundleData is correct
|
||||
bundleData ??= { graph: {}, bundles: {} };
|
||||
function pathToModule(path) {
|
||||
return path
|
||||
.replace(new RegExp(`^${opts.src}\\/`), '')
|
||||
.replace(/\.js$/, '');
|
||||
}
|
||||
for (const [path, value] of Object.entries(res.metafile.outputs)) {
|
||||
const entryModule = pathToModule(path);
|
||||
const inputModules = Object.keys(value.inputs).map(pathToModule);
|
||||
bundleData.bundles[entryModule] = inputModules;
|
||||
}
|
||||
for (const [input, value] of Object.entries(res.metafile.inputs)) {
|
||||
const dependencies = value.imports.map(i => pathToModule(i.path));
|
||||
bundleData.graph[pathToModule(input)] = dependencies;
|
||||
}
|
||||
}
|
||||
for (const file of res.outputFiles) {
|
||||
let contents = file.contents;
|
||||
if (file.path.endsWith('.js')) {
|
||||
if (opts.fileContentMapper) {
|
||||
// UGLY the fileContentMapper is per file but at this point we have all files
|
||||
// bundled already. So, we call the mapper for the same contents but each file
|
||||
// that has been included in the bundle...
|
||||
let newText = file.text;
|
||||
for (const input of Object.keys(res.metafile.inputs)) {
|
||||
newText = opts.fileContentMapper(newText, input);
|
||||
}
|
||||
contents = Buffer.from(newText);
|
||||
}
|
||||
}
|
||||
files.push(new VinylFile({
|
||||
contents: Buffer.from(contents),
|
||||
path: file.path,
|
||||
base: path.join(REPO_ROOT_PATH, opts.src)
|
||||
}));
|
||||
}
|
||||
});
|
||||
// await task; // FORCE serial bundling (makes debugging easier)
|
||||
tasks.push(task);
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
return { files, bundleData };
|
||||
};
|
||||
bundleAsync().then((output) => {
|
||||
// bundle output (JS, CSS, SVG...)
|
||||
es.readArray(output.files).pipe(bundlesStream);
|
||||
// bundeInfo.json
|
||||
const bundleInfoArray = [];
|
||||
if (typeof output.bundleData === 'object') {
|
||||
bundleInfoArray.push(new VinylFile({
|
||||
path: 'bundleInfo.json',
|
||||
base: '.',
|
||||
contents: Buffer.from(JSON.stringify(output.bundleData, null, '\t'))
|
||||
}));
|
||||
}
|
||||
es.readArray(bundleInfoArray).pipe(bundleInfoStream);
|
||||
// forward all resources
|
||||
gulp.src(opts.resources, { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream);
|
||||
});
|
||||
const result = es.merge(bundlesStream, resourcesStream, bundleInfoStream);
|
||||
return result
|
||||
.pipe(sourcemaps.write('./', {
|
||||
sourceRoot: undefined,
|
||||
addComment: true,
|
||||
includeContent: true
|
||||
}))
|
||||
.pipe(opts.languages && opts.languages.length ? (0, i18n_1.processNlsFiles)({
|
||||
out: opts.src,
|
||||
fileHeader: bundledFileHeader,
|
||||
languages: opts.languages
|
||||
}) : es.through());
|
||||
}
|
||||
function optimizeCommonJSTask(opts) {
|
||||
const esbuild = require('esbuild');
|
||||
const src = opts.src;
|
||||
|
@ -226,9 +383,15 @@ function optimizeLoaderTask(src, out, bundleLoader, bundledFileHeader = '', exte
|
|||
}
|
||||
function optimizeTask(opts) {
|
||||
return function () {
|
||||
const optimizers = [optimizeAMDTask(opts.amd)];
|
||||
if (opts.commonJS) {
|
||||
optimizers.push(optimizeCommonJSTask(opts.commonJS));
|
||||
const optimizers = [];
|
||||
if ((0, esm_1.isESM)('Running optimizer in ESM mode')) {
|
||||
optimizers.push(optimizeESMTask(opts.amd, opts.commonJS));
|
||||
}
|
||||
else {
|
||||
optimizers.push(optimizeAMDTask(opts.amd));
|
||||
if (opts.commonJS) {
|
||||
optimizers.push(optimizeCommonJSTask(opts.commonJS));
|
||||
}
|
||||
}
|
||||
if (opts.manual) {
|
||||
optimizers.push(optimizeManualTask(opts.manual));
|
||||
|
|
|
@ -17,6 +17,8 @@ import { Language, processNlsFiles } from './i18n';
|
|||
import { createStatsStream } from './stats';
|
||||
import * as util from './util';
|
||||
import { gulpPostcss } from './postcss';
|
||||
import type { Plugin } from 'esbuild';
|
||||
import { isESM } from './esm';
|
||||
|
||||
const REPO_ROOT_PATH = path.join(__dirname, '../..');
|
||||
|
||||
|
@ -213,7 +215,7 @@ const DEFAULT_FILE_HEADER = [
|
|||
|
||||
function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream {
|
||||
const src = opts.src;
|
||||
const entryPoints = opts.entryPoints;
|
||||
const entryPoints = opts.entryPoints.filter(d => d.target !== 'esm');
|
||||
const resources = opts.resources;
|
||||
const loaderConfig = opts.loaderConfig;
|
||||
const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER;
|
||||
|
@ -271,6 +273,202 @@ function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream {
|
|||
}) : es.through());
|
||||
}
|
||||
|
||||
function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJSTaskOpts): NodeJS.ReadWriteStream {
|
||||
// TODO@esm honor IEntryPoint#prepred/append (unused?)
|
||||
|
||||
const esbuild = require('esbuild') as typeof import('esbuild');
|
||||
|
||||
const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER;
|
||||
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
|
||||
|
||||
const resourcesStream = es.through(); // this stream will contain the resources
|
||||
const bundlesStream = es.through(); // this stream will contain the bundled files
|
||||
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
|
||||
|
||||
const entryPoints = opts.entryPoints.filter(d => d.target !== 'amd');
|
||||
if (cjsOpts) {
|
||||
cjsOpts.entryPoints.forEach(entryPoint => entryPoints.push({ name: path.parse(entryPoint).name }));
|
||||
}
|
||||
|
||||
// TODO@esm remove hardcoded entry point and support `dest` of `IEntryPoint` or clean that up
|
||||
entryPoints.push({ name: 'vs/base/worker/workerMain' });
|
||||
|
||||
const allMentionedModules = new Set<string>();
|
||||
for (const entryPoint of entryPoints) {
|
||||
allMentionedModules.add(entryPoint.name);
|
||||
entryPoint.include?.forEach(allMentionedModules.add, allMentionedModules);
|
||||
entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules);
|
||||
}
|
||||
|
||||
// TODO@esm remove this from the bundle files
|
||||
allMentionedModules.delete('vs/css');
|
||||
|
||||
const bundleAsync = async () => {
|
||||
|
||||
let bundleData: bundle.IBundleData | undefined;
|
||||
const files: VinylFile[] = [];
|
||||
const tasks: Promise<any>[] = [];
|
||||
|
||||
for (const entryPoint of entryPoints) {
|
||||
|
||||
const t1 = performance.now();
|
||||
console.log(`[bundle] STARTING '${entryPoint.name}'...`);
|
||||
|
||||
// support for 'dest' via esbuild#in/out
|
||||
const dest = entryPoint.dest?.replace(/\.[^/.]+$/, '') ?? entryPoint.name;
|
||||
// const dest = entryPoint.name;
|
||||
|
||||
// boilerplate massage
|
||||
const banner = { js: '' };
|
||||
const fs = await import('node:fs');
|
||||
const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js');
|
||||
banner.js += await fs.promises.readFile(tslibPath, 'utf-8');
|
||||
|
||||
const boilerplateTrimmer: Plugin = {
|
||||
name: 'boilerplate-trimmer',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.js$/ }, async args => {
|
||||
const contents = await fs.promises.readFile(args.path, 'utf-8');
|
||||
const newContents = bundle.removeAllTSBoilerplate(contents);
|
||||
return { contents: newContents };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// support for 'preprend' via the esbuild#banner
|
||||
if (entryPoint.prepend?.length) {
|
||||
for (const item of entryPoint.prepend) {
|
||||
const fullpath = path.join(REPO_ROOT_PATH, opts.src, item.path);
|
||||
const source = await fs.promises.readFile(fullpath, 'utf8');
|
||||
banner.js += source + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const task = esbuild.build({
|
||||
logLevel: 'silent',
|
||||
bundle: true,
|
||||
external: entryPoint.exclude,
|
||||
packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages
|
||||
platform: 'neutral', // makes esm
|
||||
format: 'esm',
|
||||
plugins: [boilerplateTrimmer],
|
||||
target: ['es2023'],
|
||||
loader: {
|
||||
'.ttf': 'file',
|
||||
'.svg': 'file',
|
||||
'.png': 'file',
|
||||
'.sh': 'file',
|
||||
},
|
||||
banner,
|
||||
entryPoints: [
|
||||
{
|
||||
in: path.join(REPO_ROOT_PATH, opts.src, `${entryPoint.name}.js`),
|
||||
out: dest,
|
||||
}
|
||||
],
|
||||
outdir: path.join(REPO_ROOT_PATH, opts.src),
|
||||
write: false, // enables res.outputFiles
|
||||
metafile: true, // enables res.metafile
|
||||
|
||||
}).then(res => {
|
||||
console.log(`[bundle] DONE for '${entryPoint.name}' (${Math.round(performance.now() - t1)}ms)`);
|
||||
|
||||
if (opts.bundleInfo) {
|
||||
// TODO@esm validate that bundleData is correct
|
||||
bundleData ??= { graph: {}, bundles: {} };
|
||||
|
||||
function pathToModule(path: string) {
|
||||
return path
|
||||
.replace(new RegExp(`^${opts.src}\\/`), '')
|
||||
.replace(/\.js$/, '');
|
||||
}
|
||||
|
||||
for (const [path, value] of Object.entries(res.metafile.outputs)) {
|
||||
const entryModule = pathToModule(path);
|
||||
const inputModules = Object.keys(value.inputs).map(pathToModule);
|
||||
bundleData.bundles[entryModule] = inputModules;
|
||||
}
|
||||
|
||||
for (const [input, value] of Object.entries(res.metafile.inputs)) {
|
||||
const dependencies = value.imports.map(i => pathToModule(i.path));
|
||||
bundleData.graph[pathToModule(input)] = dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of res.outputFiles) {
|
||||
|
||||
let contents = file.contents;
|
||||
|
||||
if (file.path.endsWith('.js')) {
|
||||
|
||||
if (opts.fileContentMapper) {
|
||||
// UGLY the fileContentMapper is per file but at this point we have all files
|
||||
// bundled already. So, we call the mapper for the same contents but each file
|
||||
// that has been included in the bundle...
|
||||
let newText = file.text;
|
||||
for (const input of Object.keys(res.metafile.inputs)) {
|
||||
newText = opts.fileContentMapper(newText, input);
|
||||
}
|
||||
contents = Buffer.from(newText);
|
||||
}
|
||||
}
|
||||
|
||||
files.push(new VinylFile({
|
||||
contents: Buffer.from(contents),
|
||||
path: file.path,
|
||||
base: path.join(REPO_ROOT_PATH, opts.src)
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// await task; // FORCE serial bundling (makes debugging easier)
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
await Promise.all(tasks);
|
||||
return { files, bundleData };
|
||||
};
|
||||
|
||||
bundleAsync().then((output) => {
|
||||
|
||||
// bundle output (JS, CSS, SVG...)
|
||||
es.readArray(output.files).pipe(bundlesStream);
|
||||
|
||||
// bundeInfo.json
|
||||
const bundleInfoArray: VinylFile[] = [];
|
||||
if (typeof output.bundleData === 'object') {
|
||||
bundleInfoArray.push(new VinylFile({
|
||||
path: 'bundleInfo.json',
|
||||
base: '.',
|
||||
contents: Buffer.from(JSON.stringify(output.bundleData, null, '\t'))
|
||||
}));
|
||||
}
|
||||
es.readArray(bundleInfoArray).pipe(bundleInfoStream);
|
||||
|
||||
// forward all resources
|
||||
gulp.src(opts.resources, { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream);
|
||||
});
|
||||
|
||||
const result = es.merge(
|
||||
bundlesStream,
|
||||
resourcesStream,
|
||||
bundleInfoStream
|
||||
);
|
||||
|
||||
return result
|
||||
.pipe(sourcemaps.write('./', {
|
||||
sourceRoot: undefined,
|
||||
addComment: true,
|
||||
includeContent: true
|
||||
}))
|
||||
.pipe(opts.languages && opts.languages.length ? processNlsFiles({
|
||||
out: opts.src,
|
||||
fileHeader: bundledFileHeader,
|
||||
languages: opts.languages
|
||||
}) : es.through());
|
||||
}
|
||||
|
||||
export interface IOptimizeCommonJSTaskOpts {
|
||||
/**
|
||||
* The paths to consider for optimizing.
|
||||
|
@ -360,9 +558,15 @@ export interface IOptimizeTaskOpts {
|
|||
|
||||
export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream {
|
||||
return function () {
|
||||
const optimizers = [optimizeAMDTask(opts.amd)];
|
||||
if (opts.commonJS) {
|
||||
optimizers.push(optimizeCommonJSTask(opts.commonJS));
|
||||
const optimizers: NodeJS.ReadWriteStream[] = [];
|
||||
if (isESM('Running optimizer in ESM mode')) {
|
||||
optimizers.push(optimizeESMTask(opts.amd, opts.commonJS));
|
||||
} else {
|
||||
optimizers.push(optimizeAMDTask(opts.amd));
|
||||
|
||||
if (opts.commonJS) {
|
||||
optimizers.push(optimizeCommonJSTask(opts.commonJS));
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.manual) {
|
||||
|
|
|
@ -15,6 +15,7 @@ const dep_lists_2 = require("./rpm/dep-lists");
|
|||
const types_1 = require("./debian/types");
|
||||
const types_2 = require("./rpm/types");
|
||||
const product = require("../../product.json");
|
||||
const esm_1 = require("../lib/esm");
|
||||
// A flag that can easily be toggled.
|
||||
// Make sure to compile the build directory after toggling the value.
|
||||
// If false, we warn about new dependencies if they show up
|
||||
|
@ -43,7 +44,8 @@ async function getDependencies(packageType, buildDir, applicationName, arch) {
|
|||
throw new Error('Invalid RPM arch string ' + arch);
|
||||
}
|
||||
// Get the files for which we want to find dependencies.
|
||||
const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked');
|
||||
const canAsar = !(0, esm_1.isESM)('ASAR disabled in Linux builds'); // TODO@esm ASAR disabled in ESM
|
||||
const nativeModulesPath = path.join(buildDir, 'resources', 'app', canAsar ? 'node_modules.asar.unpacked' : 'node_modules');
|
||||
const findResult = (0, child_process_1.spawnSync)('find', [nativeModulesPath, '-name', '*.node']);
|
||||
if (findResult.status) {
|
||||
console.error('Error finding files:');
|
||||
|
|
|
@ -15,6 +15,7 @@ import { referenceGeneratedDepsByArch as rpmGeneratedDeps } from './rpm/dep-list
|
|||
import { DebianArchString, isDebianArchString } from './debian/types';
|
||||
import { isRpmArchString, RpmArchString } from './rpm/types';
|
||||
import product = require('../../product.json');
|
||||
import { isESM } from '../lib/esm';
|
||||
|
||||
// A flag that can easily be toggled.
|
||||
// Make sure to compile the build directory after toggling the value.
|
||||
|
@ -47,7 +48,8 @@ export async function getDependencies(packageType: 'deb' | 'rpm', buildDir: stri
|
|||
}
|
||||
|
||||
// Get the files for which we want to find dependencies.
|
||||
const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked');
|
||||
const canAsar = !isESM('ASAR disabled in Linux builds'); // TODO@esm ASAR disabled in ESM
|
||||
const nativeModulesPath = path.join(buildDir, 'resources', 'app', canAsar ? 'node_modules.asar.unpacked' : 'node_modules');
|
||||
const findResult = spawnSync('find', [nativeModulesPath, '-name', '*.node']);
|
||||
if (findResult.status) {
|
||||
console.error('Error finding files:');
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
// *****************************************************************
|
||||
// * *
|
||||
// * AMD-TO-ESM MIGRATION SCRIPT *
|
||||
// * *
|
||||
// *****************************************************************
|
||||
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { join, extname, dirname, relative } from 'node:path';
|
||||
import { preProcessFile } from 'typescript';
|
||||
import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// @ts-expect-error
|
||||
import watch from './build/lib/watch/index.js';
|
||||
|
||||
const enableWatching = !process.argv.includes('--disable-watch');
|
||||
|
||||
const srcFolder = fileURLToPath(new URL('src', import.meta.url));
|
||||
const dstFolder = fileURLToPath(new URL('src2', import.meta.url));
|
||||
|
||||
const binaryFileExtensions = new Set([
|
||||
'.svg', '.ttf', '.png', '.sh', '.html', '.json', '.zsh', '.scpt', '.mp3', '.fish', '.ps1', '.psm1', '.md', '.txt', '.zip', '.pdf', '.qwoff', '.jxs', '.tst', '.wuff', '.less', '.utf16le', '.snap', '.tsx'
|
||||
]);
|
||||
|
||||
function migrate() {
|
||||
console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`);
|
||||
console.log(`STARTING MIGRATION of src to src2.`);
|
||||
|
||||
// installing watcher quickly to avoid missing early events
|
||||
const watchSrc = enableWatching ? watch('src/**', { base: 'src', readDelay: 200 }) : undefined;
|
||||
|
||||
/** @type {string[]} */
|
||||
const files = [];
|
||||
readdir(srcFolder, files);
|
||||
|
||||
for (const filePath of files) {
|
||||
const fileContents = readFileSync(filePath);
|
||||
migrateOne(filePath, fileContents);
|
||||
}
|
||||
|
||||
writeFileSync(join(dstFolder, '.gitignore'), `*`);
|
||||
|
||||
console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`);
|
||||
console.log(`COMPLETED MIGRATION of src to src2. You can now launch yarn watch or yarn watch-client`);
|
||||
|
||||
if (watchSrc) {
|
||||
console.log(`WATCHING src for changes...`);
|
||||
|
||||
watchSrc.on('data', (e) => {
|
||||
migrateOne(e.path, e.contents);
|
||||
console.log(`Handled change event for ${e.path}.`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function posixFilePath(filePath) {
|
||||
return filePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filePath
|
||||
* @param fileContents
|
||||
*/
|
||||
function migrateOne(filePath, fileContents) {
|
||||
const fileExtension = extname(filePath);
|
||||
|
||||
if (fileExtension === '.ts') {
|
||||
migrateTS(filePath, fileContents.toString());
|
||||
} else if (fileExtension === '.js' || fileExtension === '.cjs') {
|
||||
if (
|
||||
posixFilePath(filePath).endsWith('vs/loader.js')
|
||||
) {
|
||||
// fake loader
|
||||
writeDestFile(filePath, `(function () {
|
||||
if (typeof require !== 'undefined') {
|
||||
return;
|
||||
}
|
||||
globalThis.require = function () {
|
||||
console.trace('[require(...)] this is ESM, no more AMD/CJS require');
|
||||
};
|
||||
globalThis.require.config = function () {
|
||||
console.trace('[require.config(...)] this is ESM, no more AMD/CJS require');
|
||||
};
|
||||
})();`);
|
||||
|
||||
} else {
|
||||
writeDestFile(filePath, fileContents);
|
||||
}
|
||||
} else if (fileExtension === '.mjs') {
|
||||
writeDestFile(filePath, fileContents);
|
||||
} else if (fileExtension === '.css') {
|
||||
writeDestFile(filePath, fileContents);
|
||||
} else if (filePath.endsWith('tsconfig.base.json')) {
|
||||
const opts = JSON.parse(fileContents.toString());
|
||||
opts.compilerOptions.module = 'ESNext';
|
||||
opts.compilerOptions.allowSyntheticDefaultImports = true;
|
||||
writeDestFile(filePath, JSON.stringify(opts, null, '\t'));
|
||||
} else if (binaryFileExtensions.has(fileExtension)) {
|
||||
writeDestFile(filePath, fileContents);
|
||||
} else {
|
||||
console.log(`ignoring ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileContents
|
||||
* @typedef {{pos:number;end:number;}} Import
|
||||
* @return
|
||||
*/
|
||||
function discoverImports(fileContents) {
|
||||
const info = preProcessFile(fileContents);
|
||||
const search = /export .* from ['"]([^'"]+)['"]/g;
|
||||
/** typedef {Import[]} */
|
||||
let result = [];
|
||||
do {
|
||||
const m = search.exec(fileContents);
|
||||
if (!m) {
|
||||
break;
|
||||
}
|
||||
const end = m.index + m[0].length - 2;
|
||||
const pos = end - m[1].length;
|
||||
result.push({ pos, end });
|
||||
} while (true);
|
||||
|
||||
result = result.concat(info.importedFiles);
|
||||
|
||||
result.sort((a, b) => {
|
||||
return a.pos - b.pos;
|
||||
});
|
||||
for (let i = 1; i < result.length; i++) {
|
||||
const prev = result[i - 1];
|
||||
const curr = result[i];
|
||||
if (prev.pos === curr.pos) {
|
||||
result.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filePath
|
||||
* @param fileContents
|
||||
*/
|
||||
function migrateTS(filePath, fileContents) {
|
||||
const filePathPosix = posixFilePath(filePath);
|
||||
if (filePath.endsWith('.d.ts')) {
|
||||
return writeDestFile(filePath, fileContents);
|
||||
}
|
||||
|
||||
const imports = discoverImports(fileContents);
|
||||
/** @type {Replacement[]} */
|
||||
const replacements = [];
|
||||
for (let i = imports.length - 1; i >= 0; i--) {
|
||||
const pos = imports[i].pos + 1;
|
||||
const end = imports[i].end + 1;
|
||||
const importedFilename = fileContents.substring(pos, end);
|
||||
|
||||
/** @type {string} */
|
||||
let importedFilepath;
|
||||
if (/^vs\/css!/.test(importedFilename)) {
|
||||
importedFilepath = importedFilename.substr('vs/css!'.length) + '.css';
|
||||
} else {
|
||||
importedFilepath = importedFilename;
|
||||
}
|
||||
|
||||
/** @type {boolean} */
|
||||
let isRelativeImport;
|
||||
if (/(^\.\/)|(^\.\.\/)/.test(importedFilepath)) {
|
||||
importedFilepath = join(dirname(filePath), importedFilepath);
|
||||
isRelativeImport = true;
|
||||
} else if (/^vs\//.test(importedFilepath)) {
|
||||
importedFilepath = join(srcFolder, importedFilepath);
|
||||
isRelativeImport = true;
|
||||
} else {
|
||||
importedFilepath = importedFilepath;
|
||||
isRelativeImport = false;
|
||||
}
|
||||
|
||||
/** @type {string} */
|
||||
let replacementImport;
|
||||
|
||||
if (isRelativeImport) {
|
||||
replacementImport = generateRelativeImport(filePath, importedFilepath);
|
||||
} else {
|
||||
replacementImport = importedFilepath;
|
||||
}
|
||||
|
||||
replacements.push({ pos, end, text: replacementImport });
|
||||
}
|
||||
|
||||
// replacements = replacements.concat(rewriteDefaultImports(fileContents));
|
||||
|
||||
fileContents = applyReplacements(fileContents, replacements);
|
||||
|
||||
fileContents = fileContents.replace(/require\.__\$__nodeRequire/g, 'require');
|
||||
|
||||
|
||||
writeDestFile(filePath, fileContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filePath
|
||||
* @param importedFilepath
|
||||
*/
|
||||
function generateRelativeImport(filePath, importedFilepath) {
|
||||
/** @type {string} */
|
||||
let relativePath;
|
||||
// See https://github.com/microsoft/TypeScript/issues/16577#issuecomment-754941937
|
||||
if (!importedFilepath.endsWith('.css') && !importedFilepath.endsWith('.cjs')) {
|
||||
importedFilepath = `${importedFilepath}.js`;
|
||||
}
|
||||
relativePath = relative(dirname(filePath), `${importedFilepath}`);
|
||||
relativePath = relativePath.replace(/\\/g, '/');
|
||||
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
|
||||
relativePath = './' + relativePath;
|
||||
}
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
/** @typedef {{pos:number;end:number;text:string;}} Replacement */
|
||||
|
||||
/**
|
||||
* @param str
|
||||
* @param replacements
|
||||
*/
|
||||
function applyReplacements(str, replacements) {
|
||||
replacements.sort((a, b) => {
|
||||
return a.pos - b.pos;
|
||||
});
|
||||
|
||||
/** @type {string[]} */
|
||||
const result = [];
|
||||
let lastEnd = 0;
|
||||
for (const replacement of replacements) {
|
||||
const { pos, end, text } = replacement;
|
||||
result.push(str.substring(lastEnd, pos));
|
||||
result.push(text);
|
||||
lastEnd = end;
|
||||
}
|
||||
result.push(str.substring(lastEnd, str.length));
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param srcFilePath
|
||||
* @param fileContents
|
||||
*/
|
||||
function writeDestFile(srcFilePath, fileContents) {
|
||||
const destFilePath = srcFilePath.replace(srcFolder, dstFolder);
|
||||
ensureDir(dirname(destFilePath));
|
||||
|
||||
if (/(\.ts$)|(\.js$)|(\.html$)/.test(destFilePath)) {
|
||||
fileContents = toggleComments(fileContents);
|
||||
}
|
||||
|
||||
/** @type {Buffer | undefined} */
|
||||
let existingFileContents = undefined;
|
||||
try {
|
||||
existingFileContents = readFileSync(destFilePath);
|
||||
} catch (err) { }
|
||||
if (!buffersAreEqual(existingFileContents, fileContents)) {
|
||||
writeFileSync(destFilePath, fileContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileContents
|
||||
*/
|
||||
function toggleComments(fileContents) {
|
||||
const lines = String(fileContents).split(/\r\n|\r|\n/);
|
||||
let mode = 0;
|
||||
let didChange = false;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (mode === 0) {
|
||||
if (/\/\/ ESM-comment-begin/.test(line)) {
|
||||
mode = 1;
|
||||
continue;
|
||||
}
|
||||
if (/\/\/ ESM-uncomment-begin/.test(line)) {
|
||||
mode = 2;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mode === 1) {
|
||||
if (/\/\/ ESM-comment-end/.test(line)) {
|
||||
mode = 0;
|
||||
continue;
|
||||
}
|
||||
didChange = true;
|
||||
lines[i] = '// ' + line;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mode === 2) {
|
||||
if (/\/\/ ESM-uncomment-end/.test(line)) {
|
||||
mode = 0;
|
||||
continue;
|
||||
}
|
||||
didChange = true;
|
||||
lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) {
|
||||
return indent;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (didChange) {
|
||||
return lines.join('\n');
|
||||
}
|
||||
return fileContents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param existingFileContents
|
||||
* @param fileContents
|
||||
*/
|
||||
function buffersAreEqual(existingFileContents, fileContents) {
|
||||
if (!existingFileContents) {
|
||||
return false;
|
||||
}
|
||||
if (typeof fileContents === 'string') {
|
||||
fileContents = Buffer.from(fileContents);
|
||||
}
|
||||
return existingFileContents.equals(fileContents);
|
||||
}
|
||||
|
||||
const ensureDirCache = new Set();
|
||||
function ensureDir(dirPath) {
|
||||
if (ensureDirCache.has(dirPath)) {
|
||||
return;
|
||||
}
|
||||
ensureDirCache.add(dirPath);
|
||||
ensureDir(dirname(dirPath));
|
||||
if (!existsSync(dirPath)) {
|
||||
mkdirSync(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
function readdir(dirPath, result) {
|
||||
const entries = readdirSync(dirPath);
|
||||
for (const entry of entries) {
|
||||
const entryPath = join(dirPath, entry);
|
||||
const stat = statSync(entryPath);
|
||||
if (stat.isDirectory()) {
|
||||
readdir(join(dirPath, entry), result);
|
||||
} else {
|
||||
result.push(entryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
migrate();
|
Loading…
Reference in New Issue