@@ -62,57 +62,107 @@ async function bundleTypeScriptSource(tsPath: string, opts: BuildOptions): Promi
62
62
63
63
// remove the default ts.getDefaultLibFilePath because it uses some
64
64
// node apis and we'll be replacing it with our own anyways
65
- code = removeFromSource ( code , `ts.getDefaultLibFilePath = getDefaultLibFilePath;` ) ;
65
+ // TODO(STENCIL-816): remove in-browser compilation
66
+ code = removeFromSource ( code , `getDefaultLibFilePath: () => getDefaultLibFilePath,` ) ;
66
67
67
68
// remove the CPUProfiler since it uses node apis
68
- code = removeFromSource ( code , `enableCPUProfiler: enableCPUProfiler,` ) ;
69
- code = removeFromSource ( code , `disableCPUProfiler: disableCPUProfiler,` ) ;
69
+ // TODO(STENCIL-816): remove in-browser compilation
70
+ code = removeFromSource ( code , `enableCPUProfiler,` ) ;
71
+ // TODO(STENCIL-816): remove in-browser compilation
72
+ code = removeFromSource ( code , `disableCPUProfiler,` ) ;
73
+
74
+ // As of 5.0, because typescript is now bundled with esbuild the structure of
75
+ // the file we're dealing with here (`lib/typescript.js`) has changed.
76
+ // Previously there was an iife which got an object as an argument and just
77
+ // stuck properties onto it, something like
78
+ //
79
+ // ```js
80
+ // var ts = (function (ts) {
81
+ // ts.someMethod = () => { ... };
82
+ // })(ts || ts = {});
83
+ // ```
84
+ //
85
+ // as of 5.0 it instead looks (conceptually) something like:
86
+ //
87
+ // ```js
88
+ // var ts = (function () {
89
+ // const ts = {}
90
+ // const define = (name, value) => {
91
+ // Object.defineProperty(ts, name, value, { enumerable: true })
92
+ // }
93
+ // define('someMethod', () => { ... })
94
+ // return ts;
95
+ // })();
96
+ // ```
97
+ //
98
+ // Note that the call to `Object.defineProperty` does not set `configurable` to `true`
99
+ // (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description)
100
+ // which means that later calls to do something like
101
+ //
102
+ // ```ts
103
+ // import ts from 'typescript';
104
+ //
105
+ // ts.someMethod = function myReplacementForSomeMethod () {
106
+ // ...
107
+ // };
108
+ // ```
109
+ //
110
+ // will fail because without `configurable: true` you can't re-assign
111
+ // properties.
112
+ //
113
+ // All well and good, except for the fact that our patching of typescript to
114
+ // use for instance the in-memory file system depends on us being able to
115
+ // monkey-patch typescript in exactly this way. So in order to retain our
116
+ // current approach to patching TypeScript we need to edit this file in order
117
+ // to add `configurable: true` to the options passed to
118
+ // `Object.defineProperty`:
119
+ const TS_PROP_DEFINER = `__defProp(target, name, { get: all[name], enumerable: true });` ;
120
+ const TS_PROP_DEFINER_RECONFIGURABLE = `__defProp(target, name, { get: all[name], enumerable: true, configurable: true });` ;
121
+
122
+ code = code . replace ( TS_PROP_DEFINER , TS_PROP_DEFINER_RECONFIGURABLE ) ;
123
+
124
+ const jestTypesciptFilename = join ( opts . scriptsBuildDir , 'typescript-modified-for-jest.js' ) ;
125
+ await fs . writeFile ( jestTypesciptFilename , code ) ;
126
+
127
+ // Here we transform the TypeScript source from a commonjs to an ES module.
128
+ // We do this so that we can add an import from the `@environment` module.
70
129
71
130
// trim off the last part that sets module.exports and polyfills globalThis since
72
131
// we don't want typescript to add itself to module.exports when in a node env
73
- const tsEnding = `})(ts || (ts = {}));` ;
132
+ const tsEnding = `if (typeof module !== "undefined" && module.exports) { module.exports = ts; }` ;
133
+
74
134
if ( ! code . includes ( tsEnding ) ) {
75
135
throw new Error ( `"${ tsEnding } " not found` ) ;
76
136
}
77
137
const lastEnding = code . lastIndexOf ( tsEnding ) ;
78
- code = code . slice ( 0 , lastEnding + tsEnding . length ) ;
79
-
80
- // there's a billion unnecessary "var ts;" for namespaces
81
- // but we'll be using the top level "const ts" instead
82
- code = code . replace ( / v a r t s ; / g, '' ) ;
138
+ code = code . slice ( 0 , lastEnding ) ;
83
139
84
- // minification is crazy better if it doesn't use typescript's
85
- // namespace closures, like (function(ts) {...})(ts = ts || {});
86
- code = code . replace ( / \| \| \( t s \= \{ \} \) / g, '' ) ;
87
-
88
- // make a nice clean default export
89
- // "process.browser" is used by typescript to know if it should use the node sys or not
90
140
const o : string [ ] = [ ] ;
91
141
o . push ( `// TypeScript ${ opts . typescriptVersion } ` ) ;
92
142
o . push ( `import { IS_NODE_ENV } from '@environment';` ) ;
93
143
o . push ( `process.browser = !IS_NODE_ENV;` ) ;
94
- o . push ( `const ts = {};` ) ;
95
144
o . push ( code ) ;
96
145
o . push ( `export default ts;` ) ;
97
146
code = o . join ( '\n' ) ;
98
147
99
- const { minify } = await import ( 'terser' ) ;
100
-
101
- if ( opts . isProd ) {
102
- const minified = await minify ( code , {
103
- ecma : 2018 ,
104
- module : true ,
105
- compress : {
106
- ecma : 2018 ,
107
- passes : 2 ,
108
- } ,
109
- format : {
110
- ecma : 2018 ,
111
- comments : false ,
112
- } ,
113
- } ) ;
114
- code = minified . code ;
115
- }
148
+ // TODO(STENCIL-839): investigate minification issue w/ typescript 5.0
149
+ // const { minify } = await import('terser');
150
+
151
+ // if (opts.isProd) {
152
+ // const minified = await minify(code, {
153
+ // ecma: 2018,
154
+ // // module: true,
155
+ // compress: {
156
+ // ecma: 2018,
157
+ // passes: 2,
158
+ // },
159
+ // format: {
160
+ // ecma: 2018,
161
+ // comments: false,
162
+ // },
163
+ // });
164
+ // code = minified.code;
165
+ // }
116
166
117
167
await fs . writeFile ( cacheFile , code ) ;
118
168
0 commit comments