Skip to content

Commit 2335d95

Browse files
authored
Merge pull request #159300 from microsoft/joh/swc
SWC experiments
2 parents 6815774 + 2f390ce commit 2335d95

File tree

12 files changed

+382
-9
lines changed

12 files changed

+382
-9
lines changed

build/azure-pipelines/darwin/product-build-darwin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ steps:
170170
- script: |
171171
set -e
172172
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
173-
yarn gulp "transpile-client" "transpile-extensions"
173+
yarn gulp "transpile-client-swc" "transpile-extensions"
174174
displayName: Transpile
175175
176176
- ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}:

build/azure-pipelines/linux/product-build-linux-client.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ steps:
249249
- script: |
250250
set -e
251251
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
252-
yarn gulp "transpile-client" "transpile-extensions"
252+
yarn gulp "transpile-client-swc" "transpile-extensions"
253253
displayName: Transpile
254254
255255
- ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}:

build/azure-pipelines/win32/product-build-win32.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ steps:
163163
. build/azure-pipelines/win32/exec.ps1
164164
$ErrorActionPreference = "Stop"
165165
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
166-
exec { yarn gulp "transpile-client" "transpile-extensions" }
166+
exec { yarn gulp "transpile-client-swc" "transpile-extensions" }
167167
displayName: Transpile
168168
169169
- ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}:

build/gulpfile.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ require('events').EventEmitter.defaultMaxListeners = 100;
1111
const gulp = require('gulp');
1212
const util = require('./lib/util');
1313
const task = require('./lib/task');
14-
const { transpileTask, compileTask, watchTask, compileApiProposalNamesTask, watchApiProposalNamesTask } = require('./lib/compilation');
14+
const { transpileClientSWC, transpileTask, compileTask, watchTask, compileApiProposalNamesTask, watchApiProposalNamesTask } = require('./lib/compilation');
1515
const { monacoTypecheckTask/* , monacoTypecheckWatchTask */ } = require('./gulpfile.editor');
1616
const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } = require('./gulpfile.extensions');
1717

1818
// API proposal names
1919
gulp.task(compileApiProposalNamesTask);
2020
gulp.task(watchApiProposalNamesTask);
2121

22+
// SWC Client Transpile
23+
const transpileClientSWCTask = task.define('transpile-client-swc', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), transpileClientSWC('src', 'out')));
24+
gulp.task(transpileClientSWCTask);
25+
2226
// Transpile only
2327
const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), transpileTask('src', 'out')));
2428
gulp.task(transpileClientTask);

build/lib/compilation.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Licensed under the MIT License. See License.txt in the project root for license information.
55
*--------------------------------------------------------------------------------------------*/
66
Object.defineProperty(exports, "__esModule", { value: true });
7-
exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = exports.watchTask = exports.compileTask = exports.transpileTask = void 0;
7+
exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = exports.watchTask = exports.compileTask = exports.transpileTask = exports.transpileClientSWC = void 0;
88
const es = require("event-stream");
99
const fs = require("fs");
1010
const gulp = require("gulp");
@@ -18,7 +18,29 @@ const ansiColors = require("ansi-colors");
1818
const os = require("os");
1919
const File = require("vinyl");
2020
const task = require("./task");
21+
const swc_1 = require("./swc");
2122
const watch = require('./watch');
23+
// --- SWC: transpile -------------------------------------
24+
function transpileClientSWC(src, out) {
25+
return function () {
26+
// run SWC sync and put files straight onto the disk
27+
const swcPromise = (0, swc_1.createSwcClientStream)().exec();
28+
// copy none TS resources, like CSS, images, onto the disk
29+
const bom = require('gulp-bom');
30+
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
31+
const tsFilter = util.filter(data => !/\.ts$/.test(data.path));
32+
const srcStream = gulp.src(`${src}/**`, { base: `${src}` });
33+
const copyStream = srcStream
34+
.pipe(utf8Filter)
35+
.pipe(bom()) // this is required to preserve BOM in test files that loose it otherwise
36+
.pipe(utf8Filter.restore)
37+
.pipe(tsFilter);
38+
const copyPromise = util.streamToPromise(copyStream.pipe(gulp.dest(out)));
39+
return Promise.all([swcPromise, copyPromise]);
40+
};
41+
}
42+
exports.transpileClientSWC = transpileClientSWC;
43+
// --- gulp-tsb: compile and transpile --------------------------------
2244
const reporter = (0, reporter_1.createReporter)();
2345
function getTypeScriptCompilerOptions(src) {
2446
const rootDir = path.join(__dirname, `../../${src}`);

build/lib/compilation.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,40 @@ import * as os from 'os';
1717
import ts = require('typescript');
1818
import * as File from 'vinyl';
1919
import * as task from './task';
20-
20+
import { createSwcClientStream } from './swc';
2121
const watch = require('./watch');
2222

23+
24+
// --- SWC: transpile -------------------------------------
25+
26+
export function transpileClientSWC(src: string, out: string) {
27+
28+
return function () {
29+
30+
// run SWC sync and put files straight onto the disk
31+
const swcPromise = createSwcClientStream().exec();
32+
33+
// copy none TS resources, like CSS, images, onto the disk
34+
const bom = require('gulp-bom') as typeof import('gulp-bom');
35+
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
36+
const tsFilter = util.filter(data => !/\.ts$/.test(data.path));
37+
const srcStream = gulp.src(`${src}/**`, { base: `${src}` });
38+
39+
const copyStream = srcStream
40+
.pipe(utf8Filter)
41+
.pipe(bom()) // this is required to preserve BOM in test files that loose it otherwise
42+
.pipe(utf8Filter.restore)
43+
.pipe(tsFilter);
44+
45+
const copyPromise = util.streamToPromise(copyStream.pipe(gulp.dest(out)));
46+
47+
return Promise.all([swcPromise, copyPromise]);
48+
};
49+
50+
}
51+
52+
// --- gulp-tsb: compile and transpile --------------------------------
53+
2354
const reporter = createReporter();
2455

2556
function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions {

build/lib/swc/.swcrc-amd

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"$schema": "http://json.schemastore.org/swcrc",
3+
"exclude": "\\.js$",
4+
"jsc": {
5+
"parser": {
6+
"syntax": "typescript",
7+
"tsx": false,
8+
"decorators": true
9+
},
10+
"target": "es2020",
11+
"loose": false,
12+
"minify": {
13+
"compress": false,
14+
"mangle": false
15+
}
16+
},
17+
"module": {
18+
"type": "amd",
19+
"noInterop": true
20+
},
21+
"minify": false
22+
}

build/lib/swc/.swcrc-no-mod

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"$schema": "http://json.schemastore.org/swcrc",
3+
"exclude": "\\.js$",
4+
"jsc": {
5+
"parser": {
6+
"syntax": "typescript",
7+
"tsx": false,
8+
"decorators": true
9+
},
10+
"target": "es2020",
11+
"loose": false,
12+
"minify": {
13+
"compress": false,
14+
"mangle": false
15+
}
16+
},
17+
"isModule": false,
18+
"module": {
19+
"type": "es6"
20+
},
21+
"minify": false
22+
}

build/lib/swc/index.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"use strict";
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
Object.defineProperty(exports, "__esModule", { value: true });
7+
exports.createSwcClientStream = void 0;
8+
const child_process_1 = require("child_process");
9+
const stream_1 = require("stream");
10+
const path_1 = require("path");
11+
const util = require("util");
12+
const gulp = require("gulp");
13+
/**
14+
* SWC transpile stream. Can be used as stream but `exec` is the prefered way because under the
15+
* hood this simply shells out to swc-cli. There is room for improvement but this already works.
16+
* Ideas
17+
* * use API, not swc-cli
18+
* * invoke binaries directly, don't go through swc-cli
19+
* * understand how to configure both setups in one (https://github.com/swc-project/swc/issues/4989)
20+
*/
21+
function createSwcClientStream() {
22+
const execAsync = util.promisify(child_process_1.exec);
23+
const cwd = (0, path_1.join)(__dirname, '../../../');
24+
const srcDir = (0, path_1.join)(__dirname, '../../../src');
25+
const outDir = (0, path_1.join)(__dirname, '../../../out');
26+
const pathConfigAmd = (0, path_1.join)(__dirname, '.swcrc-amd');
27+
const pathConfigNoModule = (0, path_1.join)(__dirname, '.swcrc-no-mod');
28+
return new class extends stream_1.Readable {
29+
constructor() {
30+
super({ objectMode: true, highWaterMark: Number.MAX_SAFE_INTEGER });
31+
this._isStarted = false;
32+
}
33+
async exec(print) {
34+
const t1 = Date.now();
35+
const errors = [];
36+
try {
37+
const data1 = await execAsync(`npx swc --config-file ${pathConfigAmd} ${srcDir}/ --out-dir ${outDir}`, { encoding: 'utf-8', cwd });
38+
errors.push(data1.stderr);
39+
const data2 = await execAsync(`npx swc --config-file ${pathConfigNoModule} ${srcDir}/vs/base/worker/workerMain.ts --out-dir ${outDir}`, { encoding: 'utf-8', cwd });
40+
errors.push(data2.stderr);
41+
return true;
42+
}
43+
catch (error) {
44+
console.error(errors);
45+
console.error(error);
46+
this.destroy(error);
47+
return false;
48+
}
49+
finally {
50+
if (print) {
51+
console.log(`DONE with SWC after ${Date.now() - t1}ms`);
52+
}
53+
}
54+
}
55+
async _read(_size) {
56+
if (this._isStarted) {
57+
return;
58+
}
59+
this._isStarted = true;
60+
if (!this.exec()) {
61+
this.push(null);
62+
return;
63+
}
64+
for await (const file of gulp.src(`${outDir}/**/*.js`, { base: outDir })) {
65+
this.push(file);
66+
}
67+
this.push(null);
68+
}
69+
};
70+
}
71+
exports.createSwcClientStream = createSwcClientStream;
72+
if (process.argv[1] === __filename) {
73+
createSwcClientStream().exec(true);
74+
}

build/lib/swc/index.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { exec } from 'child_process';
7+
import { Readable } from 'stream';
8+
import { join } from 'path';
9+
import * as util from 'util';
10+
import * as gulp from 'gulp';
11+
12+
/**
13+
* SWC transpile stream. Can be used as stream but `exec` is the prefered way because under the
14+
* hood this simply shells out to swc-cli. There is room for improvement but this already works.
15+
* Ideas
16+
* * use API, not swc-cli
17+
* * invoke binaries directly, don't go through swc-cli
18+
* * understand how to configure both setups in one (https://github.com/swc-project/swc/issues/4989)
19+
*/
20+
export function createSwcClientStream(): Readable & { exec(print?: boolean): Promise<boolean> } {
21+
22+
const execAsync = util.promisify(exec);
23+
24+
const cwd = join(__dirname, '../../../');
25+
const srcDir = join(__dirname, '../../../src');
26+
const outDir = join(__dirname, '../../../out');
27+
28+
const pathConfigAmd = join(__dirname, '.swcrc-amd');
29+
const pathConfigNoModule = join(__dirname, '.swcrc-no-mod');
30+
31+
return new class extends Readable {
32+
33+
private _isStarted = false;
34+
35+
constructor() {
36+
super({ objectMode: true, highWaterMark: Number.MAX_SAFE_INTEGER });
37+
}
38+
39+
async exec(print?: boolean) {
40+
const t1 = Date.now();
41+
const errors: string[] = [];
42+
try {
43+
const data1 = await execAsync(`npx swc --config-file ${pathConfigAmd} ${srcDir}/ --out-dir ${outDir}`, { encoding: 'utf-8', cwd });
44+
errors.push(data1.stderr);
45+
46+
const data2 = await execAsync(`npx swc --config-file ${pathConfigNoModule} ${srcDir}/vs/base/worker/workerMain.ts --out-dir ${outDir}`, { encoding: 'utf-8', cwd });
47+
errors.push(data2.stderr);
48+
return true;
49+
} catch (error) {
50+
console.error(errors);
51+
console.error(error);
52+
this.destroy(error);
53+
return false;
54+
} finally {
55+
if (print) {
56+
console.log(`DONE with SWC after ${Date.now() - t1}ms`);
57+
}
58+
}
59+
}
60+
61+
async _read(_size: number): Promise<void> {
62+
if (this._isStarted) {
63+
return;
64+
}
65+
this._isStarted = true;
66+
if (!this.exec()) {
67+
this.push(null);
68+
return;
69+
}
70+
for await (const file of gulp.src(`${outDir}/**/*.js`, { base: outDir })) {
71+
this.push(file);
72+
}
73+
this.push(null);
74+
}
75+
};
76+
}
77+
78+
if (process.argv[1] === __filename) {
79+
createSwcClientStream().exec(true);
80+
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@
9999
"devDependencies": {
100100
"7zip": "0.0.6",
101101
"@playwright/test": "1.24.2",
102+
"@swc/cli": "0.1.57",
103+
"@swc/core": "1.2.245",
102104
"@types/cookie": "^0.3.3",
103105
"@types/copy-webpack-plugin": "^6.0.3",
104106
"@types/cssnano": "^4.0.0",

0 commit comments

Comments
 (0)