@atlaspack/optimizer-swc 2.16.9-dev-9ef951846.0 → 2.16.10-dev-fix-sourcemaps-50acc1acf.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/SwcOptimizer.js +13 -29
- package/dist/minifyWithSourceMap.js +71 -0
- package/lib/SwcOptimizer.js +13 -47
- package/lib/minifyWithSourceMap.js +108 -0
- package/lib/types/minifyWithSourceMap.d.ts +40 -0
- package/package.json +6 -6
- package/src/SwcOptimizer.ts +18 -29
- package/src/minifyWithSourceMap.ts +104 -0
- package/test/minifyWithSourceMap.test.ts +370 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
package/dist/SwcOptimizer.js
CHANGED
|
@@ -36,13 +36,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const nullthrows_1 = __importDefault(require("nullthrows"));
|
|
40
|
-
const core_1 = require("@swc/core");
|
|
41
39
|
const plugin_1 = require("@atlaspack/plugin");
|
|
42
40
|
const utils_1 = require("@atlaspack/utils");
|
|
43
|
-
const source_map_1 = __importDefault(require("@atlaspack/source-map"));
|
|
44
41
|
const diagnostic_1 = __importStar(require("@atlaspack/diagnostic"));
|
|
45
42
|
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const minifyWithSourceMap_1 = require("./minifyWithSourceMap");
|
|
46
44
|
exports.default = new plugin_1.Optimizer({
|
|
47
45
|
async loadConfig({ config, options }) {
|
|
48
46
|
let userConfig = await config.getConfigFrom(path_1.default.join(options.projectRoot, 'index'), ['.terserrc', '.terserrc.js', '.terserrc.cjs', '.terserrc.mjs']);
|
|
@@ -55,23 +53,15 @@ exports.default = new plugin_1.Optimizer({
|
|
|
55
53
|
let code = await (0, utils_1.blobToString)(contents);
|
|
56
54
|
let result;
|
|
57
55
|
try {
|
|
58
|
-
result = await (0,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
bundle.env.outputFormat === 'commonjs',
|
|
68
|
-
module: bundle.env.outputFormat === 'esmodule',
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
minify: true,
|
|
72
|
-
sourceMaps: !!bundle.env.sourceMap,
|
|
73
|
-
configFile: false,
|
|
74
|
-
swcrc: false,
|
|
56
|
+
result = await (0, minifyWithSourceMap_1.minifyWithSourceMap)(code, bundle.env.sourceMap ? (originalMap ?? null) : null, {
|
|
57
|
+
target: 'es2022',
|
|
58
|
+
mangle: true,
|
|
59
|
+
compress: true,
|
|
60
|
+
toplevel: bundle.env.outputFormat === 'esmodule' ||
|
|
61
|
+
bundle.env.outputFormat === 'commonjs',
|
|
62
|
+
module: bundle.env.outputFormat === 'esmodule',
|
|
63
|
+
userConfig: userConfig,
|
|
64
|
+
projectRoot: options.projectRoot,
|
|
75
65
|
});
|
|
76
66
|
}
|
|
77
67
|
catch (err) {
|
|
@@ -124,15 +114,9 @@ exports.default = new plugin_1.Optimizer({
|
|
|
124
114
|
}
|
|
125
115
|
throw err;
|
|
126
116
|
}
|
|
127
|
-
let
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (resultMap) {
|
|
131
|
-
sourceMap = new source_map_1.default(options.projectRoot);
|
|
132
|
-
sourceMap.addVLQMap(JSON.parse(resultMap));
|
|
133
|
-
if (originalMap) {
|
|
134
|
-
sourceMap.extends(originalMap);
|
|
135
|
-
}
|
|
117
|
+
let minifiedContents = result.code;
|
|
118
|
+
const sourceMap = result.map;
|
|
119
|
+
if (sourceMap) {
|
|
136
120
|
let sourcemapReference = await getSourceMapReference(sourceMap);
|
|
137
121
|
if (sourcemapReference) {
|
|
138
122
|
minifiedContents += `\n//# sourceMappingURL=${sourcemapReference}\n`;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.minifyWithSourceMap = minifyWithSourceMap;
|
|
7
|
+
const nullthrows_1 = __importDefault(require("nullthrows"));
|
|
8
|
+
const core_1 = require("@swc/core");
|
|
9
|
+
const source_map_1 = __importDefault(require("@atlaspack/source-map"));
|
|
10
|
+
/**
|
|
11
|
+
* Minify `code` with SWC and produce a SourceMap whose `original*` positions
|
|
12
|
+
* point back to the sources described by `originalMap`.
|
|
13
|
+
*
|
|
14
|
+
* When `originalMap` is provided, we hand its VLQ-encoded form to SWC via the
|
|
15
|
+
* `inputSourceMap` option. SWC then composes its own (minified -> code) map
|
|
16
|
+
* with the input (code -> original sources) map per-token during
|
|
17
|
+
* minification, producing a final map that points straight back to the
|
|
18
|
+
* original sources.
|
|
19
|
+
*
|
|
20
|
+
* This is much more accurate than composing the maps *after* the fact with
|
|
21
|
+
* `SourceMap.extends(originalMap)`. The post-hoc `extends` approach calls
|
|
22
|
+
* `findClosestMapping`, which snaps each minified token to the previous
|
|
23
|
+
* mapping in the input map. When the input map has many mappings on a single
|
|
24
|
+
* generated line (which is the norm – every asset's tokens get packed onto
|
|
25
|
+
* the same generated line by the packager), "the previous mapping" is often
|
|
26
|
+
* the wrong source/line entirely, producing systematically misaligned maps.
|
|
27
|
+
*/
|
|
28
|
+
async function minifyWithSourceMap(code, originalMap, options = {}) {
|
|
29
|
+
const { target = 'es2022', mangle = true, compress = true, toplevel = false, module: isModule = false, userConfig = {}, projectRoot = '/', } = options;
|
|
30
|
+
let inputSourceMap;
|
|
31
|
+
if (originalMap) {
|
|
32
|
+
// Hand the input map to swc as `inputSourceMap` so swc composes the
|
|
33
|
+
// (minified -> code) and (code -> original sources) maps together
|
|
34
|
+
// per-token during minification. This is more accurate than calling
|
|
35
|
+
// `SourceMap.extends(originalMap)` after the fact, which uses
|
|
36
|
+
// `findClosestMapping` and snaps each minified token to the *nearest*
|
|
37
|
+
// mapping in the input map – losing column precision and sometimes
|
|
38
|
+
// mis-attributing tokens across asset boundaries when the input map
|
|
39
|
+
// has gaps. It also leaks swc's placeholder `<anon>` source into the
|
|
40
|
+
// resulting map's `sources` array.
|
|
41
|
+
// swc requires `version: 3` to parse the input map. The atlaspack
|
|
42
|
+
// SourceMap.toVLQ() helper doesn't include it, so we add it here.
|
|
43
|
+
inputSourceMap = JSON.stringify({ version: 3, ...originalMap.toVLQ() });
|
|
44
|
+
}
|
|
45
|
+
const result = await (0, core_1.transform)(code, {
|
|
46
|
+
jsc: {
|
|
47
|
+
target: target,
|
|
48
|
+
minify: {
|
|
49
|
+
mangle,
|
|
50
|
+
compress,
|
|
51
|
+
...userConfig,
|
|
52
|
+
toplevel,
|
|
53
|
+
module: isModule,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
minify: true,
|
|
57
|
+
sourceMaps: true,
|
|
58
|
+
inputSourceMap,
|
|
59
|
+
configFile: false,
|
|
60
|
+
swcrc: false,
|
|
61
|
+
});
|
|
62
|
+
const minifiedCode = (0, nullthrows_1.default)(result.code);
|
|
63
|
+
if (!result.map) {
|
|
64
|
+
return { code: minifiedCode, map: null };
|
|
65
|
+
}
|
|
66
|
+
// With `inputSourceMap` provided, swc has already composed the maps;
|
|
67
|
+
// the result already references the original sources directly.
|
|
68
|
+
const sourceMap = new source_map_1.default(projectRoot);
|
|
69
|
+
sourceMap.addVLQMap(JSON.parse(result.map));
|
|
70
|
+
return { code: minifiedCode, map: sourceMap };
|
|
71
|
+
}
|
package/lib/SwcOptimizer.js
CHANGED
|
@@ -4,20 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
function _nullthrows() {
|
|
8
|
-
const data = _interopRequireDefault(require("nullthrows"));
|
|
9
|
-
_nullthrows = function () {
|
|
10
|
-
return data;
|
|
11
|
-
};
|
|
12
|
-
return data;
|
|
13
|
-
}
|
|
14
|
-
function _core() {
|
|
15
|
-
const data = require("@swc/core");
|
|
16
|
-
_core = function () {
|
|
17
|
-
return data;
|
|
18
|
-
};
|
|
19
|
-
return data;
|
|
20
|
-
}
|
|
21
7
|
function _plugin() {
|
|
22
8
|
const data = require("@atlaspack/plugin");
|
|
23
9
|
_plugin = function () {
|
|
@@ -32,13 +18,6 @@ function _utils() {
|
|
|
32
18
|
};
|
|
33
19
|
return data;
|
|
34
20
|
}
|
|
35
|
-
function _sourceMap() {
|
|
36
|
-
const data = _interopRequireDefault(require("@atlaspack/source-map"));
|
|
37
|
-
_sourceMap = function () {
|
|
38
|
-
return data;
|
|
39
|
-
};
|
|
40
|
-
return data;
|
|
41
|
-
}
|
|
42
21
|
function _diagnostic() {
|
|
43
22
|
const data = _interopRequireWildcard(require("@atlaspack/diagnostic"));
|
|
44
23
|
_diagnostic = function () {
|
|
@@ -53,8 +32,9 @@ function _path() {
|
|
|
53
32
|
};
|
|
54
33
|
return data;
|
|
55
34
|
}
|
|
56
|
-
|
|
35
|
+
var _minifyWithSourceMap = require("./minifyWithSourceMap");
|
|
57
36
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
37
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
58
38
|
var _default = exports.default = new (_plugin().Optimizer)({
|
|
59
39
|
async loadConfig({
|
|
60
40
|
config,
|
|
@@ -80,22 +60,14 @@ var _default = exports.default = new (_plugin().Optimizer)({
|
|
|
80
60
|
let code = await (0, _utils().blobToString)(contents);
|
|
81
61
|
let result;
|
|
82
62
|
try {
|
|
83
|
-
result = await (0,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
toplevel: bundle.env.outputFormat === 'esmodule' || bundle.env.outputFormat === 'commonjs',
|
|
92
|
-
module: bundle.env.outputFormat === 'esmodule'
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
minify: true,
|
|
96
|
-
sourceMaps: !!bundle.env.sourceMap,
|
|
97
|
-
configFile: false,
|
|
98
|
-
swcrc: false
|
|
63
|
+
result = await (0, _minifyWithSourceMap.minifyWithSourceMap)(code, bundle.env.sourceMap ? originalMap ?? null : null, {
|
|
64
|
+
target: 'es2022',
|
|
65
|
+
mangle: true,
|
|
66
|
+
compress: true,
|
|
67
|
+
toplevel: bundle.env.outputFormat === 'esmodule' || bundle.env.outputFormat === 'commonjs',
|
|
68
|
+
module: bundle.env.outputFormat === 'esmodule',
|
|
69
|
+
userConfig: userConfig,
|
|
70
|
+
projectRoot: options.projectRoot
|
|
99
71
|
});
|
|
100
72
|
} catch (err) {
|
|
101
73
|
// SWC doesn't give us nice error objects, so we need to parse the message.
|
|
@@ -148,15 +120,9 @@ var _default = exports.default = new (_plugin().Optimizer)({
|
|
|
148
120
|
}
|
|
149
121
|
throw err;
|
|
150
122
|
}
|
|
151
|
-
let
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (resultMap) {
|
|
155
|
-
sourceMap = new (_sourceMap().default)(options.projectRoot);
|
|
156
|
-
sourceMap.addVLQMap(JSON.parse(resultMap));
|
|
157
|
-
if (originalMap) {
|
|
158
|
-
sourceMap.extends(originalMap);
|
|
159
|
-
}
|
|
123
|
+
let minifiedContents = result.code;
|
|
124
|
+
const sourceMap = result.map;
|
|
125
|
+
if (sourceMap) {
|
|
160
126
|
let sourcemapReference = await getSourceMapReference(sourceMap);
|
|
161
127
|
if (sourcemapReference) {
|
|
162
128
|
minifiedContents += `\n//# sourceMappingURL=${sourcemapReference}\n`;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.minifyWithSourceMap = minifyWithSourceMap;
|
|
7
|
+
function _nullthrows() {
|
|
8
|
+
const data = _interopRequireDefault(require("nullthrows"));
|
|
9
|
+
_nullthrows = function () {
|
|
10
|
+
return data;
|
|
11
|
+
};
|
|
12
|
+
return data;
|
|
13
|
+
}
|
|
14
|
+
function _core() {
|
|
15
|
+
const data = require("@swc/core");
|
|
16
|
+
_core = function () {
|
|
17
|
+
return data;
|
|
18
|
+
};
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
function _sourceMap() {
|
|
22
|
+
const data = _interopRequireDefault(require("@atlaspack/source-map"));
|
|
23
|
+
_sourceMap = function () {
|
|
24
|
+
return data;
|
|
25
|
+
};
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
29
|
+
/**
|
|
30
|
+
* Minify `code` with SWC and produce a SourceMap whose `original*` positions
|
|
31
|
+
* point back to the sources described by `originalMap`.
|
|
32
|
+
*
|
|
33
|
+
* When `originalMap` is provided, we hand its VLQ-encoded form to SWC via the
|
|
34
|
+
* `inputSourceMap` option. SWC then composes its own (minified -> code) map
|
|
35
|
+
* with the input (code -> original sources) map per-token during
|
|
36
|
+
* minification, producing a final map that points straight back to the
|
|
37
|
+
* original sources.
|
|
38
|
+
*
|
|
39
|
+
* This is much more accurate than composing the maps *after* the fact with
|
|
40
|
+
* `SourceMap.extends(originalMap)`. The post-hoc `extends` approach calls
|
|
41
|
+
* `findClosestMapping`, which snaps each minified token to the previous
|
|
42
|
+
* mapping in the input map. When the input map has many mappings on a single
|
|
43
|
+
* generated line (which is the norm – every asset's tokens get packed onto
|
|
44
|
+
* the same generated line by the packager), "the previous mapping" is often
|
|
45
|
+
* the wrong source/line entirely, producing systematically misaligned maps.
|
|
46
|
+
*/
|
|
47
|
+
async function minifyWithSourceMap(code, originalMap, options = {}) {
|
|
48
|
+
const {
|
|
49
|
+
target = 'es2022',
|
|
50
|
+
mangle = true,
|
|
51
|
+
compress = true,
|
|
52
|
+
toplevel = false,
|
|
53
|
+
module: isModule = false,
|
|
54
|
+
userConfig = {},
|
|
55
|
+
projectRoot = '/'
|
|
56
|
+
} = options;
|
|
57
|
+
let inputSourceMap;
|
|
58
|
+
if (originalMap) {
|
|
59
|
+
// Hand the input map to swc as `inputSourceMap` so swc composes the
|
|
60
|
+
// (minified -> code) and (code -> original sources) maps together
|
|
61
|
+
// per-token during minification. This is more accurate than calling
|
|
62
|
+
// `SourceMap.extends(originalMap)` after the fact, which uses
|
|
63
|
+
// `findClosestMapping` and snaps each minified token to the *nearest*
|
|
64
|
+
// mapping in the input map – losing column precision and sometimes
|
|
65
|
+
// mis-attributing tokens across asset boundaries when the input map
|
|
66
|
+
// has gaps. It also leaks swc's placeholder `<anon>` source into the
|
|
67
|
+
// resulting map's `sources` array.
|
|
68
|
+
// swc requires `version: 3` to parse the input map. The atlaspack
|
|
69
|
+
// SourceMap.toVLQ() helper doesn't include it, so we add it here.
|
|
70
|
+
inputSourceMap = JSON.stringify({
|
|
71
|
+
version: 3,
|
|
72
|
+
...originalMap.toVLQ()
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const result = await (0, _core().transform)(code, {
|
|
76
|
+
jsc: {
|
|
77
|
+
target: target,
|
|
78
|
+
minify: {
|
|
79
|
+
mangle,
|
|
80
|
+
compress,
|
|
81
|
+
...userConfig,
|
|
82
|
+
toplevel,
|
|
83
|
+
module: isModule
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
minify: true,
|
|
87
|
+
sourceMaps: true,
|
|
88
|
+
inputSourceMap,
|
|
89
|
+
configFile: false,
|
|
90
|
+
swcrc: false
|
|
91
|
+
});
|
|
92
|
+
const minifiedCode = (0, _nullthrows().default)(result.code);
|
|
93
|
+
if (!result.map) {
|
|
94
|
+
return {
|
|
95
|
+
code: minifiedCode,
|
|
96
|
+
map: null
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// With `inputSourceMap` provided, swc has already composed the maps;
|
|
101
|
+
// the result already references the original sources directly.
|
|
102
|
+
const sourceMap = new (_sourceMap().default)(projectRoot);
|
|
103
|
+
sourceMap.addVLQMap(JSON.parse(result.map));
|
|
104
|
+
return {
|
|
105
|
+
code: minifiedCode,
|
|
106
|
+
map: sourceMap
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import SourceMap from '@atlaspack/source-map';
|
|
2
|
+
export type MinifyOptions = {
|
|
3
|
+
/** ES target for SWC. */
|
|
4
|
+
target?: string;
|
|
5
|
+
/** Whether to enable name mangling. */
|
|
6
|
+
mangle?: boolean;
|
|
7
|
+
/** Whether to enable compression. */
|
|
8
|
+
compress?: boolean;
|
|
9
|
+
/** Mark output as a top-level module (esmodule/commonjs). */
|
|
10
|
+
toplevel?: boolean;
|
|
11
|
+
/** Treat the input as an ES module. */
|
|
12
|
+
module?: boolean;
|
|
13
|
+
/** Extra terser-style overrides forwarded into `jsc.minify`. */
|
|
14
|
+
userConfig?: Record<string, unknown>;
|
|
15
|
+
/** Project root used when constructing the SourceMap instance. */
|
|
16
|
+
projectRoot?: string;
|
|
17
|
+
};
|
|
18
|
+
export type MinifyResult = {
|
|
19
|
+
code: string;
|
|
20
|
+
map: SourceMap | null;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Minify `code` with SWC and produce a SourceMap whose `original*` positions
|
|
24
|
+
* point back to the sources described by `originalMap`.
|
|
25
|
+
*
|
|
26
|
+
* When `originalMap` is provided, we hand its VLQ-encoded form to SWC via the
|
|
27
|
+
* `inputSourceMap` option. SWC then composes its own (minified -> code) map
|
|
28
|
+
* with the input (code -> original sources) map per-token during
|
|
29
|
+
* minification, producing a final map that points straight back to the
|
|
30
|
+
* original sources.
|
|
31
|
+
*
|
|
32
|
+
* This is much more accurate than composing the maps *after* the fact with
|
|
33
|
+
* `SourceMap.extends(originalMap)`. The post-hoc `extends` approach calls
|
|
34
|
+
* `findClosestMapping`, which snaps each minified token to the previous
|
|
35
|
+
* mapping in the input map. When the input map has many mappings on a single
|
|
36
|
+
* generated line (which is the norm – every asset's tokens get packed onto
|
|
37
|
+
* the same generated line by the packager), "the previous mapping" is often
|
|
38
|
+
* the wrong source/line entirely, producing systematically misaligned maps.
|
|
39
|
+
*/
|
|
40
|
+
export declare function minifyWithSourceMap(code: string, originalMap: SourceMap | null, options?: MinifyOptions): Promise<MinifyResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaspack/optimizer-swc",
|
|
3
|
-
"version": "2.16.
|
|
3
|
+
"version": "2.16.10-dev-fix-sourcemaps-50acc1acf.0",
|
|
4
4
|
"license": "(MIT OR Apache-2.0)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"node": ">= 16.0.0"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@atlaspack/diagnostic": "2.14.5-dev-
|
|
20
|
-
"@atlaspack/plugin": "2.14.
|
|
21
|
-
"@atlaspack/source-map": "3.3.
|
|
22
|
-
"@atlaspack/utils": "3.4.
|
|
19
|
+
"@atlaspack/diagnostic": "2.14.5-dev-fix-sourcemaps-50acc1acf.0",
|
|
20
|
+
"@atlaspack/plugin": "2.14.64-dev-fix-sourcemaps-50acc1acf.0",
|
|
21
|
+
"@atlaspack/source-map": "3.3.8-dev-fix-sourcemaps-50acc1acf.0",
|
|
22
|
+
"@atlaspack/utils": "3.4.6-dev-fix-sourcemaps-50acc1acf.0",
|
|
23
23
|
"@swc/core": "1.15.11",
|
|
24
24
|
"nullthrows": "^1.1.1"
|
|
25
25
|
},
|
|
@@ -27,5 +27,5 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build:lib": "gulp build --gulpfile ../../../gulpfile.js --cwd ."
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "50acc1acf31d5c8415a8154841a8ed09264e665a"
|
|
31
31
|
}
|
package/src/SwcOptimizer.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import nullthrows from 'nullthrows';
|
|
2
|
-
import {transform} from '@swc/core';
|
|
3
1
|
import {Optimizer} from '@atlaspack/plugin';
|
|
4
2
|
import {blobToString, stripAnsi} from '@atlaspack/utils';
|
|
5
|
-
import SourceMap from '@atlaspack/source-map';
|
|
6
3
|
import ThrowableDiagnostic, {escapeMarkdown} from '@atlaspack/diagnostic';
|
|
7
4
|
import path from 'path';
|
|
8
5
|
|
|
6
|
+
import {minifyWithSourceMap} from './minifyWithSourceMap';
|
|
7
|
+
|
|
9
8
|
export default new Optimizer({
|
|
10
9
|
async loadConfig({config, options}) {
|
|
11
10
|
let userConfig = await config.getConfigFrom(
|
|
@@ -30,25 +29,21 @@ export default new Optimizer({
|
|
|
30
29
|
let code = await blobToString(contents);
|
|
31
30
|
let result;
|
|
32
31
|
try {
|
|
33
|
-
result = await
|
|
34
|
-
|
|
32
|
+
result = await minifyWithSourceMap(
|
|
33
|
+
code,
|
|
34
|
+
bundle.env.sourceMap ? (originalMap ?? null) : null,
|
|
35
|
+
{
|
|
35
36
|
target: 'es2022',
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
module: bundle.env.outputFormat === 'esmodule',
|
|
45
|
-
},
|
|
37
|
+
mangle: true,
|
|
38
|
+
compress: true,
|
|
39
|
+
toplevel:
|
|
40
|
+
bundle.env.outputFormat === 'esmodule' ||
|
|
41
|
+
bundle.env.outputFormat === 'commonjs',
|
|
42
|
+
module: bundle.env.outputFormat === 'esmodule',
|
|
43
|
+
userConfig: userConfig as Record<string, unknown> | undefined,
|
|
44
|
+
projectRoot: options.projectRoot,
|
|
46
45
|
},
|
|
47
|
-
|
|
48
|
-
sourceMaps: !!bundle.env.sourceMap,
|
|
49
|
-
configFile: false,
|
|
50
|
-
swcrc: false,
|
|
51
|
-
});
|
|
46
|
+
);
|
|
52
47
|
} catch (err: any) {
|
|
53
48
|
// SWC doesn't give us nice error objects, so we need to parse the message.
|
|
54
49
|
let message = escapeMarkdown(
|
|
@@ -107,15 +102,9 @@ export default new Optimizer({
|
|
|
107
102
|
throw err;
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (resultMap) {
|
|
114
|
-
sourceMap = new SourceMap(options.projectRoot);
|
|
115
|
-
sourceMap.addVLQMap(JSON.parse(resultMap));
|
|
116
|
-
if (originalMap) {
|
|
117
|
-
sourceMap.extends(originalMap);
|
|
118
|
-
}
|
|
105
|
+
let minifiedContents: string = result.code;
|
|
106
|
+
const sourceMap = result.map;
|
|
107
|
+
if (sourceMap) {
|
|
119
108
|
let sourcemapReference = await getSourceMapReference(sourceMap);
|
|
120
109
|
if (sourcemapReference) {
|
|
121
110
|
minifiedContents += `\n//# sourceMappingURL=${sourcemapReference}\n`;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import nullthrows from 'nullthrows';
|
|
2
|
+
import {transform} from '@swc/core';
|
|
3
|
+
import SourceMap from '@atlaspack/source-map';
|
|
4
|
+
|
|
5
|
+
export type MinifyOptions = {
|
|
6
|
+
/** ES target for SWC. */
|
|
7
|
+
target?: string;
|
|
8
|
+
/** Whether to enable name mangling. */
|
|
9
|
+
mangle?: boolean;
|
|
10
|
+
/** Whether to enable compression. */
|
|
11
|
+
compress?: boolean;
|
|
12
|
+
/** Mark output as a top-level module (esmodule/commonjs). */
|
|
13
|
+
toplevel?: boolean;
|
|
14
|
+
/** Treat the input as an ES module. */
|
|
15
|
+
module?: boolean;
|
|
16
|
+
/** Extra terser-style overrides forwarded into `jsc.minify`. */
|
|
17
|
+
userConfig?: Record<string, unknown>;
|
|
18
|
+
/** Project root used when constructing the SourceMap instance. */
|
|
19
|
+
projectRoot?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type MinifyResult = {
|
|
23
|
+
code: string;
|
|
24
|
+
map: SourceMap | null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Minify `code` with SWC and produce a SourceMap whose `original*` positions
|
|
29
|
+
* point back to the sources described by `originalMap`.
|
|
30
|
+
*
|
|
31
|
+
* When `originalMap` is provided, we hand its VLQ-encoded form to SWC via the
|
|
32
|
+
* `inputSourceMap` option. SWC then composes its own (minified -> code) map
|
|
33
|
+
* with the input (code -> original sources) map per-token during
|
|
34
|
+
* minification, producing a final map that points straight back to the
|
|
35
|
+
* original sources.
|
|
36
|
+
*
|
|
37
|
+
* This is much more accurate than composing the maps *after* the fact with
|
|
38
|
+
* `SourceMap.extends(originalMap)`. The post-hoc `extends` approach calls
|
|
39
|
+
* `findClosestMapping`, which snaps each minified token to the previous
|
|
40
|
+
* mapping in the input map. When the input map has many mappings on a single
|
|
41
|
+
* generated line (which is the norm – every asset's tokens get packed onto
|
|
42
|
+
* the same generated line by the packager), "the previous mapping" is often
|
|
43
|
+
* the wrong source/line entirely, producing systematically misaligned maps.
|
|
44
|
+
*/
|
|
45
|
+
export async function minifyWithSourceMap(
|
|
46
|
+
code: string,
|
|
47
|
+
originalMap: SourceMap | null,
|
|
48
|
+
options: MinifyOptions = {},
|
|
49
|
+
): Promise<MinifyResult> {
|
|
50
|
+
const {
|
|
51
|
+
target = 'es2022',
|
|
52
|
+
mangle = true,
|
|
53
|
+
compress = true,
|
|
54
|
+
toplevel = false,
|
|
55
|
+
module: isModule = false,
|
|
56
|
+
userConfig = {},
|
|
57
|
+
projectRoot = '/',
|
|
58
|
+
} = options;
|
|
59
|
+
|
|
60
|
+
let inputSourceMap: string | undefined;
|
|
61
|
+
if (originalMap) {
|
|
62
|
+
// Hand the input map to swc as `inputSourceMap` so swc composes the
|
|
63
|
+
// (minified -> code) and (code -> original sources) maps together
|
|
64
|
+
// per-token during minification. This is more accurate than calling
|
|
65
|
+
// `SourceMap.extends(originalMap)` after the fact, which uses
|
|
66
|
+
// `findClosestMapping` and snaps each minified token to the *nearest*
|
|
67
|
+
// mapping in the input map – losing column precision and sometimes
|
|
68
|
+
// mis-attributing tokens across asset boundaries when the input map
|
|
69
|
+
// has gaps. It also leaks swc's placeholder `<anon>` source into the
|
|
70
|
+
// resulting map's `sources` array.
|
|
71
|
+
// swc requires `version: 3` to parse the input map. The atlaspack
|
|
72
|
+
// SourceMap.toVLQ() helper doesn't include it, so we add it here.
|
|
73
|
+
inputSourceMap = JSON.stringify({version: 3, ...originalMap.toVLQ()});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = await transform(code, {
|
|
77
|
+
jsc: {
|
|
78
|
+
target: target as any,
|
|
79
|
+
minify: {
|
|
80
|
+
mangle,
|
|
81
|
+
compress,
|
|
82
|
+
...(userConfig as object),
|
|
83
|
+
toplevel,
|
|
84
|
+
module: isModule,
|
|
85
|
+
} as any,
|
|
86
|
+
},
|
|
87
|
+
minify: true,
|
|
88
|
+
sourceMaps: true,
|
|
89
|
+
inputSourceMap,
|
|
90
|
+
configFile: false,
|
|
91
|
+
swcrc: false,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const minifiedCode: string = nullthrows(result.code);
|
|
95
|
+
if (!result.map) {
|
|
96
|
+
return {code: minifiedCode, map: null};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// With `inputSourceMap` provided, swc has already composed the maps;
|
|
100
|
+
// the result already references the original sources directly.
|
|
101
|
+
const sourceMap = new SourceMap(projectRoot);
|
|
102
|
+
sourceMap.addVLQMap(JSON.parse(result.map));
|
|
103
|
+
return {code: minifiedCode, map: sourceMap};
|
|
104
|
+
}
|