Skip to content

Commit 0e3552d

Browse files
authored
build(cdn): Ensure ES5 bundles do not use non-ES5 code (#7550)
1 parent 21dd20d commit 0e3552d

File tree

16 files changed

+255
-28
lines changed

16 files changed

+255
-28
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,10 @@ jobs:
321321
uses: ./.github/actions/restore-cache
322322
env:
323323
DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }}
324-
- name: Run linter
324+
- name: Lint source files
325325
run: yarn lint
326+
- name: Validate ES5 builds
327+
run: yarn validate:es5
326328

327329
job_circular_dep_check:
328330
name: Circular Dependency Check

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"link:yarn": "lerna exec yarn link",
2424
"lint": "lerna run lint",
2525
"lint:eslint": "lerna run lint:eslint",
26+
"validate:es5": "lerna run validate:es5",
2627
"postpublish": "lerna run --stream --concurrency 1 postpublish",
2728
"test": "lerna run --ignore @sentry-internal/* test",
2829
"test:unit": "lerna run --ignore @sentry-internal/* test:unit",
@@ -89,6 +90,7 @@
8990
"chai": "^4.1.2",
9091
"codecov": "^3.6.5",
9192
"deepmerge": "^4.2.2",
93+
"es-check": "7.1.0",
9294
"eslint": "7.32.0",
9395
"jest": "^27.5.1",
9496
"jest-environment-node": "^27.5.1",

packages/browser/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"lint": "run-s lint:prettier lint:eslint",
6767
"lint:eslint": "eslint . --format stylish",
6868
"lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"",
69+
"validate:es5": "es-check es5 build/bundles/bundle.es5.js",
6970
"size:check": "run-p size:check:es5 size:check:es6",
7071
"size:check:es5": "cat build/bundles/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES5: \",$1,\"kB\";}'",
7172
"size:check:es6": "cat build/bundles/bundle.es6.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES6: \",$1,\"kB\";}'",

packages/eslint-config-sdk/src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ module.exports = {
161161

162162
// All imports should be accounted for
163163
'import/no-extraneous-dependencies': 'error',
164+
165+
// Do not allow usage of functions we do not polyfill for ES5
166+
'@sentry-internal/sdk/no-unsupported-es6-methods': 'error',
164167
},
165168
},
166169
{

packages/eslint-plugin-sdk/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ module.exports = {
1313
'no-optional-chaining': require('./rules/no-optional-chaining'),
1414
'no-nullish-coalescing': require('./rules/no-nullish-coalescing'),
1515
'no-eq-empty': require('./rules/no-eq-empty'),
16+
'no-unsupported-es6-methods': require('./rules/no-unsupported-es6-methods'),
1617
},
1718
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
/**
4+
* Taken and adapted from https://github.com/nkt/eslint-plugin-es5/blob/master/src/rules/no-es6-methods.js
5+
*/
6+
7+
module.exports = {
8+
meta: {
9+
docs: {
10+
description: 'Forbid methods added in ES6 which are not polyfilled by Sentry.',
11+
},
12+
schema: [],
13+
},
14+
create(context) {
15+
return {
16+
CallExpression(node) {
17+
if (!node.callee || !node.callee.property) {
18+
return;
19+
}
20+
const functionName = node.callee.property.name;
21+
22+
const es6ArrayFunctions = ['copyWithin', 'values', 'fill'];
23+
const es6StringFunctions = ['repeat'];
24+
25+
const es6Functions = [].concat(es6ArrayFunctions, es6StringFunctions);
26+
if (es6Functions.indexOf(functionName) > -1) {
27+
context.report({
28+
node: node.callee.property,
29+
message: `ES6 methods not allowed: ${functionName}`,
30+
});
31+
}
32+
},
33+
};
34+
},
35+
};

packages/node/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ module.exports = {
66
rules: {
77
'@sentry-internal/sdk/no-optional-chaining': 'off',
88
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
9+
'@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
910
},
1011
};

packages/overhead-metrics/.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'import/no-unresolved': 'off',
1111
'@sentry-internal/sdk/no-optional-chaining': 'off',
1212
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
13+
'@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
1314
'jsdoc/require-jsdoc': 'off',
1415
},
1516
},

packages/replay/.eslintrc.js

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,8 @@ module.exports = {
77
extends: ['../../.eslintrc.js'],
88
overrides: [
99
{
10-
files: ['worker/**/*.ts'],
11-
parserOptions: {
12-
// TODO: figure out if we need a worker-specific tsconfig
13-
project: ['tsconfig.worker.json'],
14-
},
15-
rules: {
16-
// We cannot use backticks, as that conflicts with the stringified worker
17-
'prefer-template': 'off',
18-
},
19-
},
20-
{
21-
files: ['src/worker/**/*.js'],
22-
parserOptions: {
23-
sourceType: 'module',
24-
},
10+
files: ['src/**/*.ts'],
11+
rules: {},
2512
},
2613
{
2714
files: ['jest.setup.ts', 'jest.config.ts'],

packages/svelte/.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ module.exports = {
33
browser: true,
44
},
55
extends: ['../../.eslintrc.js'],
6+
rules: {
7+
'@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
8+
},
69
};

packages/tracing/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"lint": "run-s lint:prettier lint:eslint",
4747
"lint:eslint": "eslint . --format stylish",
4848
"lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"",
49+
"validate:es5": "es-check es5 build/bundles/bundle.tracing.es5.js",
4950
"test:unit": "jest",
5051
"test": "jest",
5152
"test:watch": "jest --watch",

packages/vue/src/components.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const ANONYMOUS_COMPONENT_NAME = '<Anonymous>';
99

1010
const repeat = (str: string, n: number): string => {
1111
// string.repeat() is not supported by IE11, we fall back to just using the string in that case
12+
// eslint-disable-next-line @sentry-internal/sdk/no-unsupported-es6-methods
1213
return str.repeat ? str.repeat(n) : str;
1314
};
1415

rollup/bundleHelpers.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
makeTerserPlugin,
1818
makeTSPlugin,
1919
makeSetSDKSourcePlugin,
20+
getEs5Polyfills,
2021
} from './plugins/index.js';
2122
import { mergePlugins } from './utils';
2223

@@ -25,6 +26,8 @@ const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js'];
2526
export function makeBaseBundleConfig(options) {
2627
const { bundleType, entrypoints, jsVersion, licenseTitle, outputFileBase, packageSpecificConfig } = options;
2728

29+
const isEs5 = jsVersion.toLowerCase() === 'es5';
30+
2831
const nodeResolvePlugin = makeNodeResolvePlugin();
2932
const sucrasePlugin = makeSucrasePlugin();
3033
const cleanupPlugin = makeCleanupPlugin();
@@ -42,6 +45,10 @@ export function makeBaseBundleConfig(options) {
4245
output: {
4346
format: 'iife',
4447
name: 'Sentry',
48+
outro: () => {
49+
// Add polyfills for ES6 array/string methods at the end of the bundle
50+
return isEs5 ? getEs5Polyfills() : '';
51+
},
4552
},
4653
context: 'window',
4754
plugins: [markAsBrowserBuildPlugin],
@@ -101,10 +108,9 @@ export function makeBaseBundleConfig(options) {
101108
strict: false,
102109
esModule: false,
103110
},
104-
plugins:
105-
jsVersion === 'es5'
106-
? [tsPlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin]
107-
: [sucrasePlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin],
111+
plugins: isEs5
112+
? [tsPlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin]
113+
: [sucrasePlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin],
108114
treeshake: 'smallest',
109115
};
110116

rollup/plugins/bundlePlugins.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
* Typescript plugin docs: https://github.com/ezolenko/rollup-plugin-typescript2
99
*/
1010

11+
import * as fs from 'fs';
12+
import * as path from 'path';
13+
1114
import commonjs from '@rollup/plugin-commonjs';
1215
import deepMerge from 'deepmerge';
1316
import license from 'rollup-plugin-license';
@@ -38,6 +41,11 @@ export function makeLicensePlugin(title) {
3841
return plugin;
3942
}
4043

44+
export function getEs5Polyfills() {
45+
// Note: __dirname resolves to e.g. packages/browser or packages/tracing
46+
return fs.readFileSync(path.join(__dirname, '../../rollup/polyfills/es5.js'), 'utf-8');
47+
}
48+
4149
/**
4250
* Create a plugin to set the value of the `__SENTRY_DEBUG__` magic string.
4351
*

rollup/polyfills/es5.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Sentry ES5 polyfills
2+
if (!('includes' in Array.prototype)) {
3+
Array.prototype.includes = function (searchElement) {
4+
return this.indexOf(searchElement) > -1;
5+
};
6+
}
7+
if (!('find' in Array.prototype)) {
8+
Array.prototype.find = function (callback) {
9+
for (var i = 0; i < this.length; i++) {
10+
if (callback(this[i])) {
11+
return this[i];
12+
}
13+
}
14+
};
15+
}
16+
if (!('findIndex' in Array.prototype)) {
17+
Array.prototype.findIndex = function (callback) {
18+
for (var i = 0; i < this.length; i++) {
19+
if (callback(this[i])) {
20+
return i;
21+
}
22+
}
23+
return -1;
24+
};
25+
}
26+
if (!('includes' in String.prototype)) {
27+
String.prototype.includes = function (searchElement) {
28+
return this.indexOf(searchElement) > -1;
29+
};
30+
}
31+
if (!('startsWith' in String.prototype)) {
32+
String.prototype.startsWith = function (searchElement) {
33+
return this.indexOf(searchElement) === 0;
34+
};
35+
}
36+
if (!('endsWith' in String.prototype)) {
37+
String.prototype.endsWith = function (searchElement) {
38+
var i = this.indexOf(searchElement);
39+
return i > -1 && i === this.length - searchElement.length;
40+
};
41+
}

0 commit comments

Comments
 (0)