From 78ea7c4de683945b759a5e3431fb4e7d9ff451c9 Mon Sep 17 00:00:00 2001 From: PaulGMardling Date: Fri, 24 Jul 2026 11:29:30 +0200 Subject: [PATCH] feat(react-storybook-addon-export-to-sandbox): remove custom-babel-loader workaround (#36385) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ...-055ab233-3768-4a82-b636-fbd44c7644a0.json | 7 ++ .../__tests__/fixtures/Example.stories.tsx | 1 + .../__tests__/integration/webpack.cjs | 85 +++++++++++++++++++ .../project.json | 8 ++ .../src/custom-babel-loader.ts | 13 --- .../src/webpack.spec.ts | 15 ++-- .../src/webpack.ts | 13 ++- 7 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 change/@fluentui-react-storybook-addon-export-to-sandbox-055ab233-3768-4a82-b636-fbd44c7644a0.json create mode 100644 packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/fixtures/Example.stories.tsx create mode 100644 packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/integration/webpack.cjs delete mode 100644 packages/react-components/react-storybook-addon-export-to-sandbox/src/custom-babel-loader.ts diff --git a/change/@fluentui-react-storybook-addon-export-to-sandbox-055ab233-3768-4a82-b636-fbd44c7644a0.json b/change/@fluentui-react-storybook-addon-export-to-sandbox-055ab233-3768-4a82-b636-fbd44c7644a0.json new file mode 100644 index 00000000000000..0ea869fa36204d --- /dev/null +++ b/change/@fluentui-react-storybook-addon-export-to-sandbox-055ab233-3768-4a82-b636-fbd44c7644a0.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "feat: run the story source transform before transpilation and remove the custom Babel loader", + "packageName": "@fluentui/react-storybook-addon-export-to-sandbox", + "email": "paulmardling@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/fixtures/Example.stories.tsx b/packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/fixtures/Example.stories.tsx new file mode 100644 index 00000000000000..9694879edc62c5 --- /dev/null +++ b/packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/fixtures/Example.stories.tsx @@ -0,0 +1 @@ +export const Example = (props: { label: string }) => ; diff --git a/packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/integration/webpack.cjs b/packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/integration/webpack.cjs new file mode 100644 index 00000000000000..d3f1ad45f8972c --- /dev/null +++ b/packages/react-components/react-storybook-addon-export-to-sandbox/__tests__/integration/webpack.cjs @@ -0,0 +1,85 @@ +const assert = require('node:assert'); +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); +const webpackCompiler = require('webpack'); + +const { webpack } = require('../../lib-commonjs/webpack.js'); + +async function main() { + const outputPath = fs.mkdtempSync(path.join(os.tmpdir(), 'export-to-sandbox-webpack-')); + const config = webpack( + { + mode: 'development', + devtool: 'source-map', + context: path.join(__dirname, '../fixtures'), + entry: './Example.stories.tsx', + output: { path: outputPath, filename: 'bundle.js' }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: { + loader: require.resolve('swc-loader'), + options: { + jsc: { + parser: { syntax: 'typescript', tsx: true }, + target: 'es2019', + }, + }, + }, + }, + ], + }, + }, + { + presetsList: [ + { + name: 'node_modules/@fluentui/react-storybook-addon-export-to-sandbox/lib/preset.js', + preset: {}, + options: { importMappings: {} }, + }, + ], + }, + ); + + try { + const stats = await compile(config); + if (stats.hasErrors()) { + throw new Error(stats.toString({ all: false, errors: true, errorDetails: true })); + } + + const bundle = fs.readFileSync(path.join(outputPath, 'bundle.js'), 'utf8'); + assert.match(bundle, /Example\.parameters\.fullSource/); + assert.match(bundle, /label: string/); + + const sourceMap = JSON.parse(fs.readFileSync(path.join(outputPath, 'bundle.js.map'), 'utf8')); + assert(sourceMap.sources.some(source => source.endsWith('Example.stories.tsx'))); + assert(sourceMap.mappings.length > 0); + } finally { + fs.rmSync(outputPath, { recursive: true, force: true }); + } +} + +function compile(config) { + const compiler = webpackCompiler(config); + + return new Promise((resolve, reject) => { + compiler.run((error, stats) => { + compiler.close(closeError => { + if (error || closeError) { + reject(error || closeError); + } else if (!stats) { + reject(new Error('Webpack compilation did not return stats.')); + } else { + resolve(stats); + } + }); + }); + }); +} + +main().catch(error => { + console.error(error); + process.exitCode = 1; +}); diff --git a/packages/react-components/react-storybook-addon-export-to-sandbox/project.json b/packages/react-components/react-storybook-addon-export-to-sandbox/project.json index 161368d74702ec..5d9a3f6f77b5a1 100644 --- a/packages/react-components/react-storybook-addon-export-to-sandbox/project.json +++ b/packages/react-components/react-storybook-addon-export-to-sandbox/project.json @@ -16,6 +16,14 @@ } ] } + }, + "test-integration": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "cwd": "{projectRoot}", + "command": "node __tests__/integration/webpack.cjs" + } } } } diff --git a/packages/react-components/react-storybook-addon-export-to-sandbox/src/custom-babel-loader.ts b/packages/react-components/react-storybook-addon-export-to-sandbox/src/custom-babel-loader.ts deleted file mode 100644 index 5a4789359c8d43..00000000000000 --- a/packages/react-components/react-storybook-addon-export-to-sandbox/src/custom-babel-loader.ts +++ /dev/null @@ -1,13 +0,0 @@ -const babelLoader = require('babel-loader'); - -/** - * Custom loader wraps the original babel-loader and fixes the incorrect `inputSourceMap` that - * is passed from a loader that uses the `sourceMap` option. - */ -module.exports = function (source: string, inputSourceMap?: object | boolean | string) { - return babelLoader.call( - this, - source, - typeof inputSourceMap === 'string' ? JSON.parse(inputSourceMap) : inputSourceMap, - ); -}; diff --git a/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.spec.ts b/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.spec.ts index 07327204e7246c..bfc9fc0e4dbf0a 100644 --- a/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.spec.ts +++ b/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.spec.ts @@ -14,11 +14,12 @@ describe(`webpack`, () => { expect(actual.module?.rules).toEqual([ { - enforce: 'post', + enforce: 'pre', test: /\.stories\.(jsx?$|tsx?$)/, use: { - loader: expect.stringContaining('custom-babel-loader'), + loader: expect.stringContaining('babel-loader'), options: { + parserOpts: { plugins: ['typescript', 'jsx'] }, plugins: [ [ expect.stringContaining('babel-preset-storybook-full-source'), @@ -52,12 +53,13 @@ describe(`webpack`, () => { expect(actual.module?.rules).toEqual([ { - enforce: 'post', + enforce: 'pre', test: /\.stories\.tsx?/, include: /foo-stories/, use: { - loader: expect.stringContaining('custom-babel-loader'), + loader: expect.stringContaining('babel-loader'), options: { + parserOpts: { plugins: ['typescript', 'jsx'] }, plugins: [ [ expect.stringContaining('babel-preset-storybook-full-source'), @@ -90,11 +92,12 @@ describe(`webpack`, () => { expect(actual.module?.rules).toEqual([ { - enforce: 'post', + enforce: 'pre', test: /\.stories\.(jsx?$|tsx?$)/, use: { - loader: expect.stringContaining('custom-babel-loader'), + loader: expect.stringContaining('babel-loader'), options: { + parserOpts: { plugins: ['typescript', 'jsx'] }, plugins: [ [ expect.stringContaining('babel-preset-storybook-full-source'), diff --git a/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.ts b/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.ts index ee906f1b6171ec..8ebe39c87ae43c 100644 --- a/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.ts +++ b/packages/react-components/react-storybook-addon-export-to-sandbox/src/webpack.ts @@ -37,17 +37,14 @@ function createBabelLoaderRule(config: Required): import('webpack' test: /\.stories\.(jsx?$|tsx?$)/, ...webpackRule, /** - * why the usage of 'post' ? - we need to run this loader after all storybook webpack rules/loaders have been executed. - * while we can use Array.prototype.unshift to "override" the indexes this approach is more declarative without additional hacks. + * Run before transpilers so this loader receives the original story source and does not depend on + * the format of sourcemaps emitted by subsequent loaders. */ - enforce: 'post', + enforce: 'pre', use: { - /** - * Custom babel loader wraps the original babel-loader and fixes the incorrect `inputSourceMap` parameter - * that is passed to babel-loader. - **/ - loader: require.resolve('./custom-babel-loader'), + loader: require.resolve('babel-loader'), options: babelLoaderOptionsUpdater({ + parserOpts: { plugins: ['typescript', 'jsx'] }, plugins: [plugin], }), },