Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack5/Vite: Fix sourcemaps #27171

Merged
merged 25 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9667b1e
Webpack5: Fix sourcemaps
valentinpalkovic May 16, 2024
19fb74d
Deprecate @storybook/csf-plugin and remove references
valentinpalkovic May 16, 2024
61ecef8
Fix addon-docs
valentinpalkovic May 16, 2024
0200052
Fix addon-docs
valentinpalkovic May 16, 2024
04378b7
Vite: Fix sourcemapping for CSF files
valentinpalkovic May 17, 2024
5cb8045
Small fixes
valentinpalkovic May 17, 2024
f81ba26
Merge branch 'next' into valentin/fix-webpack5-sourcemaps
valentinpalkovic May 21, 2024
6b47b6f
Merge remote-tracking branch 'origin/next' into valentin/fix-webpack5…
valentinpalkovic May 22, 2024
ff23e1f
Merge branch 'next' into valentin/fix-webpack5-sourcemaps
valentinpalkovic May 22, 2024
e1bbf6f
Merge branch 'next' into valentin/fix-webpack5-sourcemaps
valentinpalkovic May 22, 2024
093342f
Merge branch 'next' into valentin/fix-webpack5-sourcemaps
valentinpalkovic May 23, 2024
9f1ec03
Revert "Deprecate @storybook/csf-plugin and remove references"
valentinpalkovic May 23, 2024
8537e14
Rework csf-plugin to guarantee sourcemap functionality in all builders
valentinpalkovic May 23, 2024
13ace46
Revert: Small fixes
valentinpalkovic May 23, 2024
6c8c4ca
Revert "Vite: Fix sourcemapping for CSF files"
valentinpalkovic May 23, 2024
08cf1ad
Revert "Fix addon-docs"
valentinpalkovic May 23, 2024
aeb5c1f
Revert "Fix addon-docs"
valentinpalkovic May 23, 2024
ac6c1e5
Partially Revert "Webpack5: Fix sourcemaps"
valentinpalkovic May 23, 2024
780ef97
Fix sourcemaps across loaders and builders
valentinpalkovic May 23, 2024
aa628d6
Merge branch 'next' into valentin/fix-webpack5-sourcemaps
valentinpalkovic May 23, 2024
c5f9332
Merge branch 'next' into valentin/fix-webpack5-sourcemaps
valentinpalkovic May 23, 2024
00619f9
Fix csf plugin for webpack and rspack
valentinpalkovic May 24, 2024
a579b28
Remove includeContent option in magic string source map generation
valentinpalkovic May 24, 2024
c106a52
Fix sourcemaps for Vite
valentinpalkovic May 24, 2024
a9f1e1d
Fix sourcemaps for Webpack5
valentinpalkovic May 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ code/playwright/.cache/
code/bench-results/

/packs
code/.nx/cache
code/.nx/cache
*storybook.log
1 change: 0 additions & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
# /code/lib/core-events/ @ndelangen @kasperpeulen
# /code/lib/core-server/ @ndelangen @JReinhold @tmeasday @shilman
# /code/lib/core-webpack/ @valentinpalkovic @ndelangen
# /code/lib/csf-plugin/ @ndelangen @valentinpalkovic
# /code/lib/csf-tools/ @kasperpeulen @shilman
# /code/lib/docs-tools/ @JReinhold @shilman
# /code/lib/instrumenter/ @yannbf @kasperpeulen
Expand Down
2 changes: 1 addition & 1 deletion code/addons/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export default {
};
```

`csfPluginOptions` is an object for configuring `@storybook/csf-plugin`. When set to `null` it tells docs not to run the `csf-plugin` at all, which can be used as an optimization, or if you're already using `csf-plugin` in your `main.js`.
`csfPluginOptions` is an object for configuring using `@storybook/csf-tools` to transform story files. When set to `null` it tells docs not to run the `csf-tools` at all, which can be used as an optimization, or if you're already using `csf-tools` in your `main.js`.

> With the release of version 7.0, it is no longer possible to import `.md` files directly into Storybook using the `transcludeMarkdown` [option](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#importing-plain-markdown-files-with-transcludemarkdown-has-changed). Instead, we recommend using the [`Markdown`](https://storybook.js.org/docs/react/api/doc-block-markdown) Doc Block for importing Markdown files into your Storybook documentation.

Expand Down
28 changes: 13 additions & 15 deletions code/addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@
"require": "./dist/preview.js",
"import": "./dist/preview.mjs"
},
"./preset": {
"types": "./dist/preset.d.ts",
"require": "./dist/preset.js",
"import": "./dist/preset.js"
},
"./preset": "./dist/preset.js",
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
"./blocks": {
"types": "./dist/blocks.d.ts",
"require": "./dist/blocks.js",
Expand All @@ -51,10 +47,7 @@
"require": "./dist/preview.js",
"import": "./dist/preview.mjs"
},
"./dist/preset": {
"types": "./dist/preset.d.ts",
"require": "./dist/preset.js"
},
"./dist/preset": "./dist/preset.js",
"./dist/shims/mdx-react-shim": {
"types": "./dist/shims/mdx-react-shim.d.ts",
"require": "./dist/shims/mdx-react-shim.js",
Expand All @@ -65,6 +58,7 @@
"require": "./dist/shims/mdx-react-shim.js",
"import": "./dist/shims/mdx-react-shim.mjs"
},
"./csf-loader": "./dist/csf-loader.js",
"./mdx-loader": "./dist/mdx-loader.js",
"./svelte/HOC.svelte": "./svelte/HOC.svelte",
"./ember": "./ember/index.js",
Expand Down Expand Up @@ -95,15 +89,14 @@
],
"scripts": {
"check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts",
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
},
"dependencies": {
"@babel/core": "^7.24.4",
"@mdx-js/react": "^3.0.0",
"@storybook/blocks": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/node-logger": "workspace:*",
Expand Down Expand Up @@ -132,12 +125,17 @@
"access": "public"
},
"bundler": {
"entries": [
"exportEntries": [
"./src/index.ts",
"./src/preset.ts",
"./src/preview.ts",
"./src/shims/mdx-react-shim.ts"
],
"previewEntries": [
"./src/blocks.ts",
"./src/shims/mdx-react-shim.ts",
"./src/preview.ts"
],
"nodeEntries": [
"./src/preset.ts",
"./src/csf-loader.ts",
"./src/mdx-loader.ts"
]
},
Expand Down
40 changes: 40 additions & 0 deletions code/addons/docs/src/csf-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import fs from 'fs/promises';
import type { EnrichCsfOptions } from '@storybook/csf-tools';
import { loadCsf, formatCsf, enrichCsf } from '@storybook/csf-tools';

interface LoaderContext {
async: () => (err: Error | null, result?: string, map?: any) => void;
getOptions: () => EnrichCsfOptions;
resourcePath: string;
}

async function loader(this: LoaderContext, content: string, map: any) {
const callback = this.async();
const options = this.getOptions();
const id = this.resourcePath;

const sourceCode = await fs.readFile(id, 'utf-8');

try {
const makeTitle = (userTitle: string) => userTitle || 'default';
const csf = loadCsf(content, { makeTitle }).parse();
const csfSource = loadCsf(sourceCode, { makeTitle }).parse();
enrichCsf(csf, csfSource, options);
const formattedCsf = formatCsf(csf, { sourceMaps: true, inputSourceMap: map }, content);

if (typeof formattedCsf === 'string') {
return callback(null, formattedCsf, map);
}

callback(null, formattedCsf.code, formattedCsf.map);
} catch (err: any) {
// This can be called on legacy storiesOf files, so just ignore
// those errors. But warn about other errors.
if (!err.message?.startsWith('CSF:')) {
console.warn(err.message);
}
callback(null, content, map);
}
}

export default loader;
24 changes: 15 additions & 9 deletions code/addons/docs/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import rehypeSlug from 'rehype-slug';
import rehypeExternalLinks from 'rehype-external-links';

import type { DocsOptions, Options, PresetProperty } from '@storybook/types';
import type { CsfPluginOptions } from '@storybook/csf-plugin';
import { logger } from '@storybook/node-logger';
import type { CompileOptions } from './compiler';
import type { EnrichCsfOptions } from '@storybook/csf-tools';

/**
* Get the resolvedReact preset, which points either to
Expand All @@ -30,7 +30,7 @@ const getResolvedReact = async (options: Options) => {
async function webpack(
webpackConfig: any = {},
options: Options & {
csfPluginOptions: CsfPluginOptions | null;
csfPluginOptions: EnrichCsfOptions | null;
mdxPluginOptions?: CompileOptions;
} /* & Parameters<
typeof createCompiler
Expand Down Expand Up @@ -89,20 +89,26 @@ async function webpack(

const result = {
...webpackConfig,
plugins: [
...(webpackConfig.plugins || []),

...(csfPluginOptions
? [(await import('@storybook/csf-plugin')).webpack(csfPluginOptions)]
: []),
],
resolve: {
...webpackConfig.resolve,
alias,
},
module: {
...module,
rules: [
...(csfPluginOptions
? [
{
test: /(?<!node_modules.*)\.(story|stories)\.[tj]sx?$/,
use: [
{
loader: require.resolve('./csf-loader'),
options: csfPluginOptions,
},
],
},
]
: []),
...(module.rules || []),
{
test: /\.mdx$/,
Expand Down
1 change: 1 addition & 0 deletions code/addons/essentials/src/docs/preset.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { dirname, join } from 'path';

// @ts-expect-error no types
export * from '@storybook/addon-docs/dist/preset';

export const mdxLoaderOptions = async (config: any) => {
Expand Down
2 changes: 1 addition & 1 deletion code/addons/storysource/src/StoryPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const StoryPanel: React.FC<StoryPanelProps> = ({ api }) => {
const { source: loaderSource, locationsMap }: SourceParams = useParameter('storySource', {});
const { source: { originalSource: docsSource } = {} }: DocsParams = useParameter('docs', {});
// prefer to use the source from source-loader, but fallback to
// source provided by csf-plugin for vite usage
// source provided by csf-tools for vite usage
const source = loaderSource || docsSource || 'loading source...';
const currentLocationIndex = locationsMap
? Object.keys(locationsMap).find((key: string) => {
Expand Down
2 changes: 1 addition & 1 deletion code/builders/builder-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/preview": "workspace:*",
"@storybook/preview-api": "workspace:*",
Expand Down
38 changes: 35 additions & 3 deletions code/builders/builder-vite/src/plugins/csf-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { Plugin } from 'vite';
import { vite } from '@storybook/csf-plugin';
import type { Options } from '@storybook/types';
import fs from 'fs/promises';
import { loadCsf, enrichCsf, formatCsf } from '@storybook/csf-tools';

const STORIES_REGEX = /(?<!node_modules.*)\.(story|stories)\.[tj]sx?$/;
const logger = console;

export async function csfPlugin(config: Options): Promise<Plugin> {
const { presets } = config;
Expand All @@ -10,6 +14,34 @@ export async function csfPlugin(config: Options): Promise<Plugin> {
// @ts-expect-error - not sure what type to use here
addons.find((a) => [a, a.name].includes('@storybook/addon-docs'))?.options ?? {};

// TODO: looks like unplugin can return an array of plugins
return vite(docsOptions?.csfPluginOptions) as Plugin;
const options = docsOptions.csfPluginOptions;

return {
name: 'vite-plugin-csf',
enforce: 'pre',
async transform(code, id) {
if (!STORIES_REGEX.test(id)) {
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
return;
}

const sourceCode = await fs.readFile(id, 'utf-8');
try {
const makeTitle = (userTitle: string) => userTitle || 'default';
const csf = loadCsf(code, { makeTitle }).parse();
const csfSource = loadCsf(sourceCode, {
makeTitle,
}).parse();
enrichCsf(csf, csfSource, options);
const inputSourceMap = this.getCombinedSourcemap();
return formatCsf(csf, { sourceMaps: true, inputSourceMap }, code);
} catch (err: any) {
// This can be called on legacy storiesOf files, so just ignore
// those errors. But warn about other errors.
if (!err.message?.startsWith('CSF:')) {
logger.warn(err.message);
}
return code;
}
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ export default async function loader(
);
}

const generatedMap = magicString.generateMap({ hires: true });

return callback(null, magicString.toString(), generatedMap, meta);
return callback(null, magicString.toString(), map, meta);
} catch (err) {
return callback(null, source, map, meta);
}
Expand Down
1 change: 0 additions & 1 deletion code/frameworks/nextjs/src/swc/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export const configureSWCLoader = async (
test: /\.((c|m)?(j|t)sx?)$/,
include: [getProjectRoot()],
exclude: [/(node_modules)/, ...Object.keys(virtualModules)],
enforce: 'post',
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
use: {
// we use our own patch because we need to remove tracing from the original code
// which is not possible otherwise
Expand Down
16 changes: 5 additions & 11 deletions code/frameworks/nextjs/src/swc/next-swc-loader-patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ async function loaderTransform(this: any, parentTrace: any, source?: string, inp
const programmaticOptions = {
...swcOptions,
filename,
inputSourceMap: inputSourceMap ? JSON.stringify(inputSourceMap) : undefined,
inputSourceMap:
inputSourceMap && typeof inputSourceMap === 'object'
? JSON.stringify(inputSourceMap)
: undefined,

// Set the default sourcemap behavior based on Webpack's mapping flag,
sourceMaps: this.sourceMap,
Expand Down Expand Up @@ -166,20 +169,11 @@ export function pitch(this: any) {
}, callback);
}

function sanitizeSourceMap(rawSourceMap: any): any {
const { sourcesContent, ...sourceMap } = rawSourceMap ?? {};

// JSON parse/stringify trick required for swc to accept the SourceMap
return JSON.parse(JSON.stringify(sourceMap));
}

export default function swcLoader(this: any, inputSource: string, inputSourceMap: any) {
const loaderSpan = mockCurrentTraceSpan.traceChild('next-swc-loader');
const callback = this.async();
loaderSpan
.traceAsyncFn(() =>
loaderTransform.call(this, loaderSpan, inputSource, sanitizeSourceMap(inputSourceMap))
)
.traceAsyncFn(() => loaderTransform.call(this, loaderSpan, inputSource, inputSourceMap))
.then(
([transformedSource, outputSourceMap]: any) => {
callback(null, transformedSource, outputSourceMap || inputSourceMap);
Expand Down
2 changes: 1 addition & 1 deletion code/frameworks/react-vite/src/plugins/react-docgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export async function reactDocgen({

return {
code: s.toString(),
map: s.generateMap(),
map: s.generateMap({ hires: true, source: id }),
};
} catch (e: any) {
// Ignore the error when react-docgen cannot find a react component
Expand Down
2 changes: 1 addition & 1 deletion code/lib/csf-plugin/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CSF Plugin
# [DEPRECATED] CSF Plugin

The CSF plugin reads CSF files and enriches their content via static analysis.
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
It supports Webpack, Vite, and other bundlers using [unplugin](https://github.com/unjs/unplugin).
Expand Down
12 changes: 8 additions & 4 deletions code/lib/csf-tools/src/CsfFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,15 +583,19 @@ export const loadCsf = (code: string, options: CsfOptions) => {
interface FormatOptions {
sourceMaps?: boolean;
preserveStyle?: boolean;
inputSourceMap?: any;
}

export const formatCsf = (csf: CsfFile, options: FormatOptions = { sourceMaps: false }) => {
const result = generate.default(csf._ast, options);
export const formatCsf = (
csf: CsfFile,
options: FormatOptions = { sourceMaps: false },
code?: string
) => {
const result = generate.default(csf._ast, options, code);
if (options.sourceMaps) {
return result;
}
const { code } = result;
return code;
return result.code;
};

/**
Expand Down
2 changes: 1 addition & 1 deletion code/lib/source-loader/src/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function transform(inputSource) {

const { source, sourceJson, addsMap } = sourceObject;

// We do this so the source we display doesn't get clobbered by csf-plugin
// We do this so the source we display doesn't get clobbered by csf-tools
const rawSource = await readFile(this.resourcePath, 'utf-8');
const rawJson = sanitizeSource(rawSource);

Expand Down
7 changes: 2 additions & 5 deletions code/presets/react-webpack/src/loaders/react-docgen-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ let matchPath: TsconfigPaths.MatchPath | undefined;

export default async function reactDocgenLoader(
this: LoaderContext<{ debug: boolean }>,
source: string
source: string,
map: any
) {
const callback = this.async();
// get options
Expand Down Expand Up @@ -115,10 +116,6 @@ export default async function reactDocgenLoader(
}
});

const map = magicString.generateMap({
includeContent: true,
source: this.resourcePath,
});
callback(null, magicString.toString(), map);
} catch (error: any) {
if (error.code === ERROR_CODES.MISSING_DEFINITION) {
Expand Down
Loading