@tramvai/cli 5.46.1 → 5.47.1
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/lib/library/babel/index.d.ts +1 -1
- package/lib/library/babel/index.d.ts.map +1 -1
- package/lib/library/babel/index.js +5 -23
- package/lib/library/babel/index.js.map +1 -1
- package/lib/library/swc/index.d.ts.map +1 -1
- package/lib/library/swc/index.js +6 -20
- package/lib/library/swc/index.js.map +1 -1
- package/lib/library/webpack/application/client/common.d.ts.map +1 -1
- package/lib/library/webpack/application/client/common.js +6 -1
- package/lib/library/webpack/application/client/common.js.map +1 -1
- package/lib/library/webpack/plugins/PolyfillCondition.d.ts +8 -0
- package/lib/library/webpack/plugins/PolyfillCondition.d.ts.map +1 -0
- package/lib/library/webpack/plugins/PolyfillCondition.js +46 -0
- package/lib/library/webpack/plugins/PolyfillCondition.js.map +1 -0
- package/lib/library/webpack/utils/polyfills/const.d.ts +3 -0
- package/lib/library/webpack/utils/polyfills/const.d.ts.map +1 -0
- package/lib/library/webpack/utils/polyfills/const.js +49 -0
- package/lib/library/webpack/utils/polyfills/const.js.map +1 -0
- package/lib/library/webpack/utils/polyfills/polyfillCondition.d.ts +8 -0
- package/lib/library/webpack/utils/polyfills/polyfillCondition.d.ts.map +1 -0
- package/lib/library/webpack/utils/polyfills/polyfillCondition.js +51 -0
- package/lib/library/webpack/utils/polyfills/polyfillCondition.js.map +1 -0
- package/lib/library/webpack/utils/polyfills/specToFeature.d.ts +7 -0
- package/lib/library/webpack/utils/polyfills/specToFeature.d.ts.map +1 -0
- package/lib/library/webpack/utils/polyfills/specToFeature.js +207 -0
- package/lib/library/webpack/utils/polyfills/specToFeature.js.map +1 -0
- package/lib/library/webpack/utils/transpiler.d.ts +2 -0
- package/lib/library/webpack/utils/transpiler.d.ts.map +1 -1
- package/lib/library/webpack/utils/transpiler.js +25 -3
- package/lib/library/webpack/utils/transpiler.js.map +1 -1
- package/lib/schema/autogeneratedSchema.json +3 -6
- package/lib/typings/configEntry/cli.d.ts +1 -2
- package/lib/typings/configEntry/cli.d.ts.map +1 -1
- package/package.json +5 -3
- package/schema.json +3 -6
- package/src/library/babel/index.ts +5 -28
- package/src/library/babel/plugins/lazy-component/lazy-component.spec.ts +2 -1
- package/src/library/babel/plugins/lazy-component/legacy-universal-replace.spec.ts +2 -1
- package/src/library/swc/index.ts +7 -26
- package/src/library/webpack/application/client/common.ts +7 -1
- package/src/library/webpack/plugins/PolyfillCondition.ts +62 -0
- package/src/library/webpack/utils/polyfills/__integration__/__snapshots__/condition.test.ts.snap +1348 -0
- package/src/library/webpack/utils/polyfills/__integration__/condition.test.ts +128 -0
- package/src/library/webpack/utils/polyfills/const.ts +46 -0
- package/src/library/webpack/utils/polyfills/polyfillCondition.ts +63 -0
- package/src/library/webpack/utils/polyfills/specToFeature.ts +243 -0
- package/src/library/webpack/utils/transpiler.ts +32 -2
- package/src/schema/autogeneratedSchema.json +3 -6
- package/src/typings/configEntry/cli.ts +1 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { fastify } from 'fastify';
|
|
3
|
+
import { getPort } from '@tramvai/internal-test-utils/utils/getPort';
|
|
4
|
+
import { initPlaywright } from '@tramvai/test-pw';
|
|
5
|
+
import { getMaxBrowserVersionsByFeatures, getPolyfillCondition } from '../polyfillCondition';
|
|
6
|
+
import { getSpecToFeatureDict } from '../specToFeature';
|
|
7
|
+
|
|
8
|
+
const polyfillEntryPath = require.resolve('@tinkoff/pack-polyfills');
|
|
9
|
+
const polyfillsEntryContent = fs.readFileSync(polyfillEntryPath, 'utf-8');
|
|
10
|
+
|
|
11
|
+
// import 'core-js/modules/es.array.at'; => 'es.array.at'
|
|
12
|
+
const usedFeatures = [...polyfillsEntryContent.matchAll(/core-js\/modules\/(.*)'/gm)].map(
|
|
13
|
+
(item) => item[1]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
jest.setTimeout(3 * 60 * 1000);
|
|
17
|
+
|
|
18
|
+
describe('polyfills', () => {
|
|
19
|
+
let server;
|
|
20
|
+
let port;
|
|
21
|
+
let testUrl;
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
server = fastify();
|
|
25
|
+
port = await getPort();
|
|
26
|
+
testUrl = `http://localhost:${port}`;
|
|
27
|
+
|
|
28
|
+
server.get('*', () => '<html></html>');
|
|
29
|
+
server.listen({ port });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterAll(() => {
|
|
33
|
+
server.close();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('spec to feature generator', () => {
|
|
37
|
+
it('should generate feature: expression dictionary', () => {
|
|
38
|
+
const specToFeatureDict = getSpecToFeatureDict();
|
|
39
|
+
|
|
40
|
+
expect(specToFeatureDict).toMatchSnapshot('featureDict');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('all feature expresssion should be valid', async () => {
|
|
44
|
+
const specToFeatureDict = getSpecToFeatureDict();
|
|
45
|
+
const { getPageWrapper, browser } = await initPlaywright(testUrl);
|
|
46
|
+
|
|
47
|
+
const { page } = await getPageWrapper(testUrl);
|
|
48
|
+
|
|
49
|
+
const specExpressionsResult = await page.evaluate(
|
|
50
|
+
([specDict]) => {
|
|
51
|
+
const evalResult: Record<string, string> = {};
|
|
52
|
+
|
|
53
|
+
for (const featureName in specDict) {
|
|
54
|
+
const expression = specDict[featureName];
|
|
55
|
+
try {
|
|
56
|
+
// eslint-disable-next-line no-eval
|
|
57
|
+
const result = eval(`Boolean(${expression})`);
|
|
58
|
+
evalResult[featureName] = result;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
evalResult[featureName] = '';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return evalResult;
|
|
65
|
+
},
|
|
66
|
+
[specToFeatureDict]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(specExpressionsResult).toMatchSnapshot('expressionsResult');
|
|
70
|
+
|
|
71
|
+
const failedSpecExpressions = [];
|
|
72
|
+
|
|
73
|
+
// Неподдерживаемые в браузерах спецификации
|
|
74
|
+
const ignoredSpecs = [
|
|
75
|
+
'es.async-disposable-stack',
|
|
76
|
+
'es.disposable-stack',
|
|
77
|
+
'es.suppressed-error',
|
|
78
|
+
'es.web.immediate',
|
|
79
|
+
'web.immediate',
|
|
80
|
+
'es.observable',
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// Проверяем что не прошли проверку только esnext фичи и несколько исключений
|
|
84
|
+
for (const specName in specExpressionsResult) {
|
|
85
|
+
const specResult = specExpressionsResult[specName];
|
|
86
|
+
|
|
87
|
+
if (!specResult && !specName.includes('esnext') && !ignoredSpecs.includes(specName)) {
|
|
88
|
+
failedSpecExpressions.push(`${specName} - ${specToFeatureDict[specName]}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
expect(failedSpecExpressions.length).toBe(0);
|
|
93
|
+
|
|
94
|
+
await browser.close();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('polyfill condition', () => {
|
|
99
|
+
it('should generate max browser versions dict', () => {
|
|
100
|
+
const browserVersions = getMaxBrowserVersionsByFeatures(usedFeatures);
|
|
101
|
+
|
|
102
|
+
expect(browserVersions).toMatchSnapshot('browserVersions');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should generate valid polyfill condition', async () => {
|
|
106
|
+
const browserVersions = getMaxBrowserVersionsByFeatures(usedFeatures);
|
|
107
|
+
const polyfillCondition = getPolyfillCondition(browserVersions);
|
|
108
|
+
|
|
109
|
+
expect(polyfillCondition).toMatchSnapshot('polyfillCondition');
|
|
110
|
+
|
|
111
|
+
const { getPageWrapper, browser } = await initPlaywright(testUrl);
|
|
112
|
+
|
|
113
|
+
const { page } = await getPageWrapper(testUrl);
|
|
114
|
+
|
|
115
|
+
const polyfillConditionResult = await page.evaluate(
|
|
116
|
+
([expression]) => {
|
|
117
|
+
// eslint-disable-next-line no-eval
|
|
118
|
+
return eval(expression);
|
|
119
|
+
},
|
|
120
|
+
[polyfillCondition]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(polyfillConditionResult).toBe(false);
|
|
124
|
+
|
|
125
|
+
browser.close();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// List of ignore polyfills
|
|
2
|
+
// they are impossible or very difficult to support check in the browser
|
|
3
|
+
export const ignoredPolyfills = [
|
|
4
|
+
'es.json.to-string-tag',
|
|
5
|
+
'es.error.to-string',
|
|
6
|
+
'es.aggregate-error.cause',
|
|
7
|
+
'es.iterator',
|
|
8
|
+
'es.object.to-string',
|
|
9
|
+
'es.array.species',
|
|
10
|
+
'es.date.to-primitive',
|
|
11
|
+
'es.function.has-instance',
|
|
12
|
+
'es.string.at-alternative',
|
|
13
|
+
'es.math.to-string-tag',
|
|
14
|
+
'esnext.math.to-string-tag',
|
|
15
|
+
'es.reflect.to-string-tag',
|
|
16
|
+
'esnext.reflect.to-string-tag',
|
|
17
|
+
'esnext.function.metadata',
|
|
18
|
+
'esnext.composite-key',
|
|
19
|
+
'esnext.composite-symbol',
|
|
20
|
+
'web.dom-collections.for-each',
|
|
21
|
+
'web.dom-collections.iterator',
|
|
22
|
+
'web.dom-exception.to-string-tag',
|
|
23
|
+
'web.dom-exception.stack',
|
|
24
|
+
'es.array.unscopables.flat-map',
|
|
25
|
+
'es.array.unscopables.flat',
|
|
26
|
+
'es.array.push',
|
|
27
|
+
'es.set.difference.v2',
|
|
28
|
+
'es.set.intersection.v2',
|
|
29
|
+
'es.set.is-disjoint-from.v2',
|
|
30
|
+
'es.set.is-subset-of.v2',
|
|
31
|
+
'es.set.is-superset-of.v2',
|
|
32
|
+
'es.set.symmetric-difference.v2',
|
|
33
|
+
'es.set.union.v2',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const browsers = [
|
|
37
|
+
'chrome',
|
|
38
|
+
'safari',
|
|
39
|
+
'firefox',
|
|
40
|
+
'opera',
|
|
41
|
+
'edge',
|
|
42
|
+
'chrome-android',
|
|
43
|
+
'ios',
|
|
44
|
+
'opera_mobile',
|
|
45
|
+
'samsung',
|
|
46
|
+
];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import compat from 'core-js-compat';
|
|
2
|
+
import { coerce, compare } from 'semver';
|
|
3
|
+
import { getSpecToFeatureDict } from './specToFeature';
|
|
4
|
+
import { ignoredPolyfills, browsers } from './const';
|
|
5
|
+
|
|
6
|
+
type BrowserVersions = Record<string, { version: string; spec: string }>;
|
|
7
|
+
|
|
8
|
+
export function getMaxBrowserVersionsByFeatures(usedFeatures: string[]) {
|
|
9
|
+
const { data } = compat;
|
|
10
|
+
const browserVersions: BrowserVersions = {};
|
|
11
|
+
|
|
12
|
+
usedFeatures.forEach((spec) => {
|
|
13
|
+
if (ignoredPolyfills.includes(spec)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const specBrowsers = data[spec];
|
|
18
|
+
|
|
19
|
+
if (!specBrowsers) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
browsers.forEach((browserName) => {
|
|
24
|
+
const maxBrowserVersion = specBrowsers[browserName];
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
maxBrowserVersion &&
|
|
28
|
+
(!browserVersions[browserName] ||
|
|
29
|
+
compare(coerce(browserVersions[browserName].version), coerce(maxBrowserVersion)) === -1)
|
|
30
|
+
) {
|
|
31
|
+
browserVersions[browserName] = { version: maxBrowserVersion, spec };
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return browserVersions;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getPolyfillCondition(browserVersions: BrowserVersions) {
|
|
40
|
+
const supportConditions = new Set();
|
|
41
|
+
|
|
42
|
+
Object.values(browserVersions).forEach(({ spec }) =>
|
|
43
|
+
supportConditions.add(`!(${getSpecToFeature(spec)})`)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const polyfillCondition = [...supportConditions].join(' || ');
|
|
47
|
+
|
|
48
|
+
return polyfillCondition;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Converting the name of the specification into an expression for checking support in runtime
|
|
53
|
+
* es.string.at => String.prototype.at
|
|
54
|
+
* es.object.assign => Object.assign
|
|
55
|
+
*/
|
|
56
|
+
let specToFeatureDictCache;
|
|
57
|
+
function getSpecToFeature(spec) {
|
|
58
|
+
if (!specToFeatureDictCache) {
|
|
59
|
+
specToFeatureDictCache = getSpecToFeatureDict();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return specToFeatureDictCache[spec];
|
|
63
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import camelCaseName from '@tinkoff/utils/string/camelCaseName';
|
|
3
|
+
import { ignoredPolyfills } from './const';
|
|
4
|
+
|
|
5
|
+
// built-in-definitions.js file is not listed in the package.json exports of package, so it cannot be imported
|
|
6
|
+
// workaround this by using require with an absolute path to file
|
|
7
|
+
const pathToDep = require.resolve('babel-plugin-polyfill-corejs3/package.json');
|
|
8
|
+
const definitions = require(path.join(path.dirname(pathToDep), '/lib/built-in-definitions.js'));
|
|
9
|
+
|
|
10
|
+
// A list of new primitives, their support in browsers was added recently
|
|
11
|
+
const newPrimitivies = {
|
|
12
|
+
'weak-map': 'window.WeakMap',
|
|
13
|
+
'weak-set': 'window.WeakSet',
|
|
14
|
+
'async-disposable-stack': 'window.AsyncDisposableStack',
|
|
15
|
+
'disposable-stack': 'window.DisposableStack',
|
|
16
|
+
'suppressed-error': 'window.SuppressedError',
|
|
17
|
+
'web.immediate': 'window.setImmediate',
|
|
18
|
+
observable: 'window.Observable',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// A list of primitives for which there are no polyfills, but they are needed as a base for methods like JSON.stringify
|
|
22
|
+
const missingBuiltIns = {
|
|
23
|
+
...newPrimitivies,
|
|
24
|
+
'typed-array': 'window.Int8Array',
|
|
25
|
+
'uint8-array': 'window.Uint8Array',
|
|
26
|
+
string: 'window.String',
|
|
27
|
+
array: 'window.Array',
|
|
28
|
+
object: 'window.Object',
|
|
29
|
+
math: 'window.Math',
|
|
30
|
+
function: 'window.Function',
|
|
31
|
+
json: 'window.JSON',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Exclude fetch builtin, because its treated as object.constructor
|
|
35
|
+
const excludedBuiltIns = ['fetch'];
|
|
36
|
+
const isBuiltInExcluded = (builtInName) => excludedBuiltIns.includes(builtInName);
|
|
37
|
+
|
|
38
|
+
// Exclude iterators
|
|
39
|
+
// we do not provide polyfills for them and their support check is much more complicated
|
|
40
|
+
// es.iterator, es.async-iterator, esnext.async-iterator, esnext.iterator
|
|
41
|
+
const isSpecExcluded = (specName) =>
|
|
42
|
+
/(es(next)?)?\.(async-)?iterator/.test(specName) || ignoredPolyfills.includes(specName);
|
|
43
|
+
|
|
44
|
+
const specToFeatureDict = {};
|
|
45
|
+
function _addFeature(specName: string, primitive: string, method: string, isInstance: boolean) {
|
|
46
|
+
if (specToFeatureDict[specName]) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let checkExpression = isInstance
|
|
51
|
+
? `'${method}' in ${primitive}.prototype`
|
|
52
|
+
: `${primitive}.${method}`;
|
|
53
|
+
|
|
54
|
+
// For new primitives, we add a check for the existence of the primitive itself.
|
|
55
|
+
// for example WeakMap && WeakMap.from
|
|
56
|
+
// since WeakMap itself may not be supported in the browser, WeakMap check support may also be required
|
|
57
|
+
const isPrimitiveNew = Boolean(Object.values(newPrimitivies).includes(primitive));
|
|
58
|
+
if (isPrimitiveNew) {
|
|
59
|
+
checkExpression = `${primitive} && ${checkExpression}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
specToFeatureDict[specName] = checkExpression;
|
|
63
|
+
specToFeatureDict[specName.replace('es.', 'esnext.')] = checkExpression;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const addStaticFeature = (specName: string, primitive: string, method: string) =>
|
|
67
|
+
_addFeature(specName, primitive, method, false);
|
|
68
|
+
const addInstanceFeature = (specName: string, primitive: string, method: string) =>
|
|
69
|
+
_addFeature(specName, primitive, method, true);
|
|
70
|
+
|
|
71
|
+
const methods = {};
|
|
72
|
+
function addMethod(specName, expression) {
|
|
73
|
+
methods[specName] = expression;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const instanceMethods = new Set(['web.url.to-json']);
|
|
77
|
+
const globals = new Set();
|
|
78
|
+
function _addGlobal(globalList: string[], isInstance: boolean) {
|
|
79
|
+
globalList.forEach((globalSpecName) => {
|
|
80
|
+
globals.add(globalSpecName);
|
|
81
|
+
|
|
82
|
+
if (isInstance) {
|
|
83
|
+
instanceMethods.add(globalSpecName);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const addInstanceGlobal = (globalList: string[]) => _addGlobal(globalList, true);
|
|
88
|
+
const addGlobal = (globalList: string[]) => _addGlobal(globalList, false);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* For conversion, a dictionary of the form `spec: feature` needs to be created
|
|
92
|
+
* we use a file from babel-plugin-polyfill-corejs3 for this - https://github.com/babel/babel-polyfills/blob/main/packages/babel-plugin-polyfill-corejs3/src/built-in-definitions.ts
|
|
93
|
+
* as a result we get dictinary like { 'es.array.at: 'Array.prototype.at', 'es.object.assign': 'Object.assign' }
|
|
94
|
+
*/
|
|
95
|
+
export function getSpecToFeatureDict(): Record<string, string> {
|
|
96
|
+
const { BuiltIns, InstanceProperties, StaticProperties } = definitions;
|
|
97
|
+
|
|
98
|
+
// JS primitives: Number, Reflect, Set, Map...
|
|
99
|
+
const builtIns = Object.keys(BuiltIns).reduce((acc, builtInKey) => {
|
|
100
|
+
const { name: builtInName, global } = BuiltIns[builtInKey];
|
|
101
|
+
|
|
102
|
+
if (isBuiltInExcluded(builtInKey)) return acc;
|
|
103
|
+
|
|
104
|
+
addGlobal(global);
|
|
105
|
+
|
|
106
|
+
const removeBaseMethodNames = (builtInName) =>
|
|
107
|
+
builtInName.replace('.constructor', '').replace('.to-string', '');
|
|
108
|
+
|
|
109
|
+
acc[removeBaseMethodNames(builtInName)] = `window.${builtInKey}`;
|
|
110
|
+
acc[removeBaseMethodNames(builtInName).replace('es.', 'esnext.')] = `window.${builtInKey}`;
|
|
111
|
+
|
|
112
|
+
return acc;
|
|
113
|
+
}, {});
|
|
114
|
+
|
|
115
|
+
// Some primitives are missing, so we add them manually
|
|
116
|
+
Object.keys(missingBuiltIns).forEach((missingBuiltInName) => {
|
|
117
|
+
builtIns[`es.${missingBuiltInName}`] = `${missingBuiltIns[missingBuiltInName]}`;
|
|
118
|
+
builtIns[`esnext.${missingBuiltInName}`] = `${missingBuiltIns[missingBuiltInName]}`;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
function parseSpec(spec: string) {
|
|
122
|
+
// es.string.push
|
|
123
|
+
const splitName = spec.split('.');
|
|
124
|
+
// es.string
|
|
125
|
+
const builtInName = splitName.slice(0, -1).join('.');
|
|
126
|
+
// String
|
|
127
|
+
const primitiveExpr = builtIns[builtInName];
|
|
128
|
+
// push
|
|
129
|
+
const methodName = splitName.at(-1);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
builtInName,
|
|
133
|
+
primitiveExpr,
|
|
134
|
+
methodName,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Instance methods, for example [1, 2, 3].at(), 'some_string'.toLowerCase()...
|
|
139
|
+
Object.keys(InstanceProperties).forEach((instancePropertyName) => {
|
|
140
|
+
const { name: instanceSpecName, global } = InstanceProperties[instancePropertyName];
|
|
141
|
+
if (isSpecExcluded(instanceSpecName)) return;
|
|
142
|
+
|
|
143
|
+
const { primitiveExpr, methodName } = parseSpec(instanceSpecName);
|
|
144
|
+
|
|
145
|
+
addMethod(methodName, instancePropertyName);
|
|
146
|
+
addInstanceGlobal(global);
|
|
147
|
+
|
|
148
|
+
// Some instance methods have no parent
|
|
149
|
+
// for example Symbol.constructor is es.Symbol
|
|
150
|
+
if (!primitiveExpr) return;
|
|
151
|
+
|
|
152
|
+
addInstanceFeature(instanceSpecName, primitiveExpr, instancePropertyName);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Static methods for example Object.assign(), RegExp.escape()...
|
|
156
|
+
Object.keys(StaticProperties).forEach((primitiveName) => {
|
|
157
|
+
const staticPrimitiveProperties = StaticProperties[primitiveName];
|
|
158
|
+
|
|
159
|
+
Object.keys(staticPrimitiveProperties).forEach((staticPropertyName) => {
|
|
160
|
+
const { name: staticSpecName, global } = staticPrimitiveProperties[staticPropertyName];
|
|
161
|
+
if (isSpecExcluded(staticSpecName)) return;
|
|
162
|
+
|
|
163
|
+
const { methodName } = parseSpec(staticSpecName);
|
|
164
|
+
addGlobal(global);
|
|
165
|
+
addMethod(methodName, staticPropertyName);
|
|
166
|
+
|
|
167
|
+
// For some reason, several static properties have names that match a builtin
|
|
168
|
+
// we ignore such properties since their support in browsers appeared at the same time as the builtin itself
|
|
169
|
+
// for example Promise and Promise.all
|
|
170
|
+
if (builtIns[staticSpecName]) return;
|
|
171
|
+
|
|
172
|
+
addStaticFeature(staticSpecName, primitiveName, staticPropertyName);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
function isFeatureExists(specName) {
|
|
177
|
+
return Boolean(builtIns[specName]) || Boolean(specToFeatureDict[specName]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Each spec has a list of globals, which also need to be converted into an expression for support check
|
|
181
|
+
// but not all globals can have their method name determined unambiguously
|
|
182
|
+
[...globals].forEach((globalSpecName: string) => {
|
|
183
|
+
if (!isFeatureExists(globalSpecName)) {
|
|
184
|
+
if (isSpecExcluded(globalSpecName)) return;
|
|
185
|
+
|
|
186
|
+
const { methodName, primitiveExpr, builtInName } = parseSpec(globalSpecName);
|
|
187
|
+
|
|
188
|
+
// es.array-buffer.from
|
|
189
|
+
const globalNameSplit = globalSpecName.split('.');
|
|
190
|
+
|
|
191
|
+
// Trying to find a support check expression for a method from the already bypassed spec
|
|
192
|
+
// for example, array and typed-array have the findLastIndex method, but typed-array is listed in global
|
|
193
|
+
const featureMethod = methodName === 'constructor' ? 'constructor' : methods[methodName];
|
|
194
|
+
|
|
195
|
+
// We consider a method to be simple if its name does not contain a '-'
|
|
196
|
+
// every, filter, map can be left as it is, the method in the language is called the same way
|
|
197
|
+
const isMethodSimple = !/-/.test(methodName);
|
|
198
|
+
|
|
199
|
+
// Primitives don't have a method, so its length is 2
|
|
200
|
+
// for example es.symbol или es.string
|
|
201
|
+
const isPrimitive = globalNameSplit.length === 2;
|
|
202
|
+
if (isPrimitive) {
|
|
203
|
+
builtIns[globalSpecName] = `${BuiltIns[globalSpecName]}`;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Trying to determine the type of method from global static or instance
|
|
208
|
+
const globalInstancePrimitives = [
|
|
209
|
+
'es.array-buffer',
|
|
210
|
+
'es.typed-array',
|
|
211
|
+
'es.symbol',
|
|
212
|
+
'web.url-search-params',
|
|
213
|
+
'esnext.weak-map',
|
|
214
|
+
'esnext.weak-set',
|
|
215
|
+
'uint8-array',
|
|
216
|
+
];
|
|
217
|
+
const isInstanceMethod =
|
|
218
|
+
globalInstancePrimitives.includes(builtInName) || instanceMethods.has(globalSpecName);
|
|
219
|
+
|
|
220
|
+
let method: string;
|
|
221
|
+
if (featureMethod) {
|
|
222
|
+
method = featureMethod;
|
|
223
|
+
} else if (isMethodSimple) {
|
|
224
|
+
method = methodName;
|
|
225
|
+
} else if (primitiveExpr) {
|
|
226
|
+
// couldn't find a similar method in the original list and it contains '-'
|
|
227
|
+
// speculatively translating it to camelCase web.url.can-parse => URL.canParse
|
|
228
|
+
method = camelCaseName(methodName);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
isInstanceMethod
|
|
232
|
+
? addInstanceFeature(globalSpecName, primitiveExpr, method)
|
|
233
|
+
: addStaticFeature(globalSpecName, primitiveExpr, method);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return fixBrokenSpec({ ...builtIns, ...specToFeatureDict });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function fixBrokenSpec(specs: Record<string, string>) {
|
|
241
|
+
specs['es.string.italics'] = "'italics' in window.String.prototype";
|
|
242
|
+
return specs;
|
|
243
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type Config from 'webpack-chain';
|
|
2
2
|
import { sync as resolve } from 'resolve';
|
|
3
|
+
import envTargets from '@tinkoff/browserslist-config';
|
|
4
|
+
import browserslist from 'browserslist';
|
|
3
5
|
import type { ConfigManager } from '../../../config/configManager';
|
|
4
6
|
import { getSwcOptions } from '../../swc';
|
|
5
7
|
import { babelConfigFactory } from '../../babel';
|
|
@@ -10,6 +12,7 @@ import type { CliConfigEntry, ReactCompilerOptions } from '../../../typings/conf
|
|
|
10
12
|
export type TranspilerConfig = {
|
|
11
13
|
env: Env;
|
|
12
14
|
target: Target;
|
|
15
|
+
actualTarget: Target;
|
|
13
16
|
modern: boolean;
|
|
14
17
|
isServer: boolean;
|
|
15
18
|
generateDataQaTag: boolean;
|
|
@@ -23,6 +26,7 @@ export type TranspilerConfig = {
|
|
|
23
26
|
hot: boolean;
|
|
24
27
|
excludesPresetEnv: string[];
|
|
25
28
|
rootDir: string;
|
|
29
|
+
browsersListTargets: string[];
|
|
26
30
|
reactCompiler: boolean | ReactCompilerOptions;
|
|
27
31
|
/**
|
|
28
32
|
* Enable or disable `loose` transformations:
|
|
@@ -67,19 +71,43 @@ export const getTranspilerConfig = (
|
|
|
67
71
|
const {
|
|
68
72
|
generateDataQaTag,
|
|
69
73
|
alias,
|
|
74
|
+
target,
|
|
75
|
+
rootDir,
|
|
70
76
|
enableFillActionNamePlugin,
|
|
71
77
|
excludesPresetEnv,
|
|
72
78
|
experiments: { enableFillDeclareActionNamePlugin, reactCompiler },
|
|
73
79
|
} = configManager;
|
|
74
80
|
const { env, modern } = configManager;
|
|
81
|
+
const isServer = configManager.buildType === 'server';
|
|
75
82
|
|
|
76
83
|
if (alias) {
|
|
77
84
|
console.warn(`"alias" option deprecated and ignored as cli now supports baseUrl and paths from the app's tsconfig.json file.
|
|
78
85
|
Just check or add configuration to your tsconfig file and remove alias from tramvai.json`);
|
|
79
86
|
}
|
|
80
87
|
|
|
88
|
+
let actualTarget = target;
|
|
89
|
+
|
|
90
|
+
if (!target) {
|
|
91
|
+
if (isServer) {
|
|
92
|
+
actualTarget = 'node';
|
|
93
|
+
} else if (modern) {
|
|
94
|
+
actualTarget = 'modern';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const browserslistConfigRaw = browserslist.findConfig(rootDir);
|
|
99
|
+
|
|
100
|
+
// Set defaults if the explicit config for browserslist was not found or the config does not contain the necessary targets
|
|
101
|
+
const browserslistQuery =
|
|
102
|
+
browserslistConfigRaw?.[actualTarget] ?? envTargets[actualTarget] ?? envTargets.defaults;
|
|
103
|
+
|
|
104
|
+
const browsersListTargets = browserslist(browserslistQuery, {
|
|
105
|
+
mobileToDesktop: true,
|
|
106
|
+
env: actualTarget,
|
|
107
|
+
});
|
|
108
|
+
|
|
81
109
|
return {
|
|
82
|
-
isServer
|
|
110
|
+
isServer,
|
|
83
111
|
env,
|
|
84
112
|
generateDataQaTag,
|
|
85
113
|
modern,
|
|
@@ -89,7 +117,9 @@ Just check or add configuration to your tsconfig file and remove alias from tram
|
|
|
89
117
|
excludesPresetEnv,
|
|
90
118
|
enableFillActionNamePlugin,
|
|
91
119
|
rootDir: configManager.rootDir,
|
|
92
|
-
target
|
|
120
|
+
target,
|
|
121
|
+
actualTarget,
|
|
122
|
+
browsersListTargets,
|
|
93
123
|
loader: true,
|
|
94
124
|
modules: false,
|
|
95
125
|
typescript: false,
|
|
@@ -1016,8 +1016,7 @@
|
|
|
1016
1016
|
]
|
|
1017
1017
|
},
|
|
1018
1018
|
"excludesPresetEnv": {
|
|
1019
|
-
"title": "List of modules to exclude from `@babel/preset-env`",
|
|
1020
|
-
"description": "Option doesn't affect build with swc loader",
|
|
1019
|
+
"title": "List of modules to exclude from `@babel/preset-env` and `swc-loader`",
|
|
1021
1020
|
"type": "array",
|
|
1022
1021
|
"items": {
|
|
1023
1022
|
"type": "string"
|
|
@@ -1808,8 +1807,7 @@
|
|
|
1808
1807
|
"additionalProperties": false
|
|
1809
1808
|
},
|
|
1810
1809
|
"excludesPresetEnv": {
|
|
1811
|
-
"title": "List of modules to exclude from `@babel/preset-env`",
|
|
1812
|
-
"description": "Option doesn't affect build with swc loader",
|
|
1810
|
+
"title": "List of modules to exclude from `@babel/preset-env` and `swc-loader`",
|
|
1813
1811
|
"type": "array",
|
|
1814
1812
|
"items": {
|
|
1815
1813
|
"type": "string"
|
|
@@ -2560,8 +2558,7 @@
|
|
|
2560
2558
|
"additionalProperties": false
|
|
2561
2559
|
},
|
|
2562
2560
|
"excludesPresetEnv": {
|
|
2563
|
-
"title": "List of modules to exclude from `@babel/preset-env`",
|
|
2564
|
-
"description": "Option doesn't affect build with swc loader",
|
|
2561
|
+
"title": "List of modules to exclude from `@babel/preset-env` and `swc-loader`",
|
|
2565
2562
|
"type": "array",
|
|
2566
2563
|
"items": {
|
|
2567
2564
|
"type": "string"
|
|
@@ -142,8 +142,7 @@ export interface CliConfigEntry extends ConfigEntry {
|
|
|
142
142
|
*/
|
|
143
143
|
experiments: Experiments;
|
|
144
144
|
/**
|
|
145
|
-
* @title List of modules to exclude from `@babel/preset-env`
|
|
146
|
-
* @description Option doesn't affect build with swc loader
|
|
145
|
+
* @title List of modules to exclude from `@babel/preset-env` and `swc-loader`
|
|
147
146
|
*/
|
|
148
147
|
excludesPresetEnv?: string[];
|
|
149
148
|
/**
|