diff --git a/package-lock.json b/package-lock.json index 41a1c5d0da..b1677ead2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2369,6 +2369,12 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2404,6 +2410,14 @@ "callsites": "^3.0.0", "graceful-fs": "^4.1.15", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "@jest/test-result": { @@ -2729,6 +2743,12 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2779,6 +2799,12 @@ "typescript": "~4.1.3" }, "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "typescript": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", @@ -3002,6 +3028,12 @@ "@babel/types": "^7.3.0" } }, + "@types/convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha512-laiDIXqqthjJlyAMYAXOtN3N8+UlbM+KvZi4BaY5ZOekmVkBs/UxfK5O0HWeJVG2eW8F+Mu2ww13fTX+kY1FlQ==", + "dev": true + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -4548,6 +4580,15 @@ "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } } }, "eslint": { @@ -6314,6 +6355,14 @@ "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "istanbul-reports": { @@ -7468,6 +7517,14 @@ "callsites": "^3.0.0", "graceful-fs": "^4.2.4", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "@jest/test-result": { @@ -8785,6 +8842,14 @@ "callsites": "^3.0.0", "graceful-fs": "^4.2.4", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "@jest/test-result": { @@ -10165,6 +10230,14 @@ "dev": true, "requires": { "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "merge-stream": { @@ -11399,6 +11472,14 @@ "commander": "^2.20.0", "source-map": "~0.6.1", "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } } } @@ -11838,9 +11919,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true }, "source-map-resolve": { @@ -11864,6 +11945,14 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "source-map-url": { diff --git a/package.json b/package.json index 2eef800d8f..009fe08fa1 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "license": "MIT", "devDependencies": { "@microsoft/api-extractor": "^7.13.2", + "@types/convert-source-map": "^1.5.1", "@types/jest": "^24.0.11", "@types/json-stringify-safe": "^5.0.0", "@types/nanoid": "^2.1.0", @@ -46,6 +47,7 @@ "prettier": "^2.2.1", "react": "^16.8.6", "rollup-plugin-strip-code": "^0.2.6", + "source-map": "^0.7.3", "terser": "^5.6.1", "tsdx": "^0.14.1", "tslib": "^1.10.0", @@ -53,8 +55,8 @@ "typings-tester": "^0.3.2" }, "scripts": { - "build-ci": "node scripts/build.js && tsc && tsc -p src/query/tsconfig.json && api-extractor run", - "build": "node scripts/build.js && tsc && tsc -p src/query/tsconfig.json && api-extractor run --local", + "build-ci": "node scripts/cli.js && tsc && tsc -p src/query/tsconfig.json && api-extractor run", + "build": "node scripts/cli.js && tsc && tsc -p src/query/tsconfig.json && api-extractor run --local", "dev": "tsdx watch --format cjs,esm,system,umd", "format": "prettier --write \"src/**/*.ts\" \"**/*.md\"", "format:check": "prettier --list-different \"src/**/*.ts\" \"docs/*/**.md\"", @@ -96,4 +98,4 @@ "doc": "docs", "example": "example" } -} +} \ No newline at end of file diff --git a/scripts/build.js b/scripts/build.ts similarity index 65% rename from scripts/build.js rename to scripts/build.ts index 3aabd6ed34..9d780b3ed0 100644 --- a/scripts/build.js +++ b/scripts/build.ts @@ -1,18 +1,25 @@ +/* eslint-disable import/first */ // @ts-check -const { build, transform } = require('esbuild') -const terser = require('terser') -const rollup = require('rollup') -const path = require('path') -const fs = require('fs-extra') -const ts = require('typescript') -const { fromJSON } = require('convert-source-map') -const merge = require('merge-source-map') -const { extractInlineSourcemap, removeInlineSourceMap } = require('./sourcemap') -const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) +import { build } from 'esbuild' +import terser from 'terser' +import rollup from 'rollup' +import path from 'path' +import fs from 'fs-extra' +import MagicString from 'magic-string' +import { appendInlineSourceMap, getLocation } from './sourcemap' +import ts from 'typescript' +import { RawSourceMap, SourceMapConsumer } from 'source-map' +import merge from 'merge-source-map' +import { extractInlineSourcemap, removeInlineSourceMap } from './sourcemap' +import type { BuildOptions } from './types' +import assert from 'assert' +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const outputDir = path.join(__dirname, '../dist') -async function bundle(options) { + +async function bundle(options: BuildOptions) { const { format, minify, env, name, target } = options const result = await build({ + logLevel: 'silent', entryPoints: ['src/index.ts'], outfile: `dist/redux-toolkit${name}.js`, write: false, @@ -60,7 +67,7 @@ async function bundle(options) { }) for (const chunk of result.outputFiles) { - origin = chunk.text + const origin = chunk.text const sourcemap = extractInlineSourcemap(origin) const result = ts.transpileModule(removeInlineSourceMap(origin), { compilerOptions: { @@ -72,32 +79,67 @@ async function bundle(options) { : ts.ScriptTarget.ES5, }, }) + const mergedSourcemap = merge(sourcemap, result.sourceMapText) let code = result.outputText // TODO Is this used at all? - let mapping = mergedSourcemap + let mapping: RawSourceMap = mergedSourcemap + if (minify) { - const transformResult = await terser.minify(code, { - sourceMap: true, - output: { - comments: false, - }, - compress: { - keep_infinity: true, - pure_getters: true, - passes: 10, - }, - ecma: 5, - toplevel: true, - }) + const transformResult = await terser.minify( + appendInlineSourceMap(code, mapping), + { + sourceMap: { content: 'inline', asObject: true } as any, + output: { + comments: false, + }, + compress: { + keep_infinity: true, + pure_getters: true, + passes: 10, + }, + ecma: 5, + toplevel: true, + } + ) code = transformResult.code - mapping = transformResult.map + mapping = transformResult.map as RawSourceMap } await fs.writeFile(chunk.path, code) - console.log('path:', chunk.path) - await fs.writeJSON(chunk.path + '.map', mergedSourcemap) + await fs.writeJSON(chunk.path + '.map', mapping) + const smc = await new SourceMapConsumer(mapping) + const stubMap = { + '../src/configureStore.ts': [ + `"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers`, + ], + } + for (const [source, stubList] of Object.entries(stubMap)) { + for (const stub of stubList) { + const originContent = smc.sourceContentFor(source) + const originLocation = getLocation(originContent, stub) + const bundledPosition = getLocation(code, stub) + const recoverLocation = smc.originalPositionFor({ + line: bundledPosition.line, + column: bundledPosition.column, + }) + assert.deepStrictEqual( + source, + recoverLocation.source, + `sourceFile: expected ${source} but got ${recoverLocation.source}` + ) + assert( + Math.abs(originLocation.line - recoverLocation.line) <= 1, + `line: expected ${originLocation.line} but got ${recoverLocation.line}` + ) + assert( + Math.abs(originLocation.column - recoverLocation.column) <= 1, + `column: expected ${originLocation.column} but got ${recoverLocation.column}` + ) + } + } } } + /** * since esbuild doesn't support umd, we use rollup to convert esm to umd */ @@ -145,11 +187,12 @@ if (process.env.NODE_ENV === 'production') { }` ) } + async function main() { console.log('dir:', outputDir) await fs.remove(outputDir) await fs.ensureDir(outputDir) - const buildTargets = [ + const buildTargets: BuildOptions[] = [ { format: 'cjs', name: '.cjs.development', diff --git a/scripts/cli.js b/scripts/cli.js new file mode 100644 index 0000000000..a6105435ac --- /dev/null +++ b/scripts/cli.js @@ -0,0 +1,2 @@ +require('./register') // must be the first +require('./build.ts') diff --git a/scripts/moduld.d.ts b/scripts/moduld.d.ts new file mode 100644 index 0000000000..a538020861 --- /dev/null +++ b/scripts/moduld.d.ts @@ -0,0 +1,7 @@ +declare module 'merge-source-map' { + import { RawSourceMap } from 'source-map' + export default function merge( + map1: string | RawSourceMap, + map2: string | RawSourceMap + ): RawSourceMap +} diff --git a/scripts/register.js b/scripts/register.js new file mode 100644 index 0000000000..24f60a2728 --- /dev/null +++ b/scripts/register.js @@ -0,0 +1,11 @@ +const esbuild = require('esbuild') +const fs = require('fs') +require.extensions['.ts'] = (mod, filename) => { + const ts = fs.readFileSync(filename, 'utf-8') + const { code } = esbuild.transformSync(ts, { + loader: 'ts', + target: 'es2017', + format: 'cjs', + }) + mod._compile(code, filename) +} diff --git a/scripts/sourcemap.js b/scripts/sourcemap.js deleted file mode 100644 index 07df8f9558..0000000000 --- a/scripts/sourcemap.js +++ /dev/null @@ -1,31 +0,0 @@ -const { fromObject, fromComment } = require('convert-source-map') -const merge = require('merge-source-map') -const SOURCEMAPPING_URL = 'sourceMappingURL' - -const SOURCEMAP_REG = new RegExp( - `^\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, - 'gm' -) -function appendInlineSourceMap(code, sourceMap) { - if (sourceMap) { - const mapping = fromObject(sourceMap) - return `${code}\n${mapping.toComment()}` - } else { - return code - } -} -function removeInlineSourceMap(code) { - return code.replace( - new RegExp(`^\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'gm'), - '' - ) -} -function extractInlineSourcemap(code) { - return fromComment(code.match(SOURCEMAP_REG)?.[0]).toObject() -} - -module.exports = { - extractInlineSourcemap, - removeInlineSourceMap, - appendInlineSourceMap, -} diff --git a/scripts/sourcemap.ts b/scripts/sourcemap.ts new file mode 100644 index 0000000000..e1b09f8333 --- /dev/null +++ b/scripts/sourcemap.ts @@ -0,0 +1,47 @@ +import { fromObject, fromComment } from 'convert-source-map' +const SOURCEMAPPING_URL = 'sourceMappingURL' + +const SOURCEMAP_REG = new RegExp( + `^\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, + 'gm' +) +function appendInlineSourceMap(code: string, sourceMap: any) { + if (sourceMap) { + const mapping = fromObject(sourceMap) + return `${code}\n${mapping.toComment()}` + } else { + return code + } +} +function removeInlineSourceMap(code) { + return code.replace( + new RegExp(`^\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'gm'), + '' + ) +} +function extractInlineSourcemap(code: string) { + return fromComment(code.match(SOURCEMAP_REG)?.[0]).toObject() +} +function getLocation( + source: string, + search: string +): { line: number; column: number } { + const outIndex = source.indexOf(search) + if (outIndex < 0) { + throw new Error(`Failed to find ${search} in output`) + } + const outLines = source.slice(0, outIndex).split('\n') + const outLine = outLines.length + const outColumn = outLines[outLines.length - 1].length + return { + line: outLine, + column: outColumn, + } +} + +export { + extractInlineSourcemap, + removeInlineSourceMap, + appendInlineSourceMap, + getLocation, +} diff --git a/scripts/types.ts b/scripts/types.ts new file mode 100644 index 0000000000..37dba1493b --- /dev/null +++ b/scripts/types.ts @@ -0,0 +1,15 @@ +export interface BuildOptions { + format: 'cjs' | 'umd' | 'esm' + name: + | '.cjs.development' + | '.cjs.production.min' + | '.esm' + | '.modern' + | '.modern.development' + | '.modern.production.min' + | '.umd' + | '.umd.min' + minify: boolean + env: 'development' | 'production' | '' + target?: 'es2017' +}