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

Vite: Fix missing source map warning #28984

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getFrameworkName, loadPreviewOrConfigFile } from 'storybook/internal/co
import type { Options, PreviewAnnotation } from 'storybook/internal/types';

import { processPreviewAnnotation } from './utils/process-preview-annotation';
import { virtualAddonSetupFile, virtualStoriesFile } from './virtual-file-names';
import { SB_VIRTUAL_FILES, getResolvedVirtualModuleId } from './virtual-file-names';

export async function generateModernIframeScriptCode(options: Options, projectRoot: string) {
const { presets, configDir } = options;
Expand Down Expand Up @@ -45,7 +45,7 @@ export async function generateModernIframeScriptCode(options: Options, projectRo

return `
if (import.meta.hot) {
import.meta.hot.accept('${virtualStoriesFile}', (newModule) => {
import.meta.hot.accept('${getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)}', (newModule) => {
// importFn has changed so we need to patch the new one in
window.__STORYBOOK_PREVIEW__.onStoriesChanged({ importFn: newModule.importFn });
});
Expand All @@ -68,8 +68,8 @@ export async function generateModernIframeScriptCode(options: Options, projectRo
*/
const code = `
import { composeConfigs, PreviewWeb, ClientApi } from 'storybook/internal/preview-api';
import '${virtualAddonSetupFile}';
import { importFn } from '${virtualStoriesFile}';
import '${SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE}';
import { importFn } from '${SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE}';

${getPreviewAnnotationsFunction}

Expand Down
39 changes: 19 additions & 20 deletions code/builders/builder-vite/src/plugins/code-generator-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ import { generateImportFnScriptCode } from '../codegen-importfn-script';
import { generateModernIframeScriptCode } from '../codegen-modern-iframe-script';
import { generateAddonSetupCode } from '../codegen-set-addon-channel';
import { transformIframeHtml } from '../transform-iframe-html';
import {
virtualAddonSetupFile,
virtualFileId,
virtualPreviewFile,
virtualStoriesFile,
} from '../virtual-file-names';
import { SB_VIRTUAL_FILES, getResolvedVirtualModuleId } from '../virtual-file-names';

export function codeGeneratorPlugin(options: Options): Plugin {
const iframePath = require.resolve('@storybook/builder-vite/input/iframe.html');
Expand All @@ -28,11 +23,15 @@ export function codeGeneratorPlugin(options: Options): Plugin {
// invalidate the whole vite-app.js script on every file change.
// (this might be a little too aggressive?)
server.watcher.on('change', () => {
const appModule = server.moduleGraph.getModuleById(virtualFileId);
const appModule = server.moduleGraph.getModuleById(
getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_APP_FILE)
);
if (appModule) {
server.moduleGraph.invalidateModule(appModule);
}
const storiesModule = server.moduleGraph.getModuleById(virtualStoriesFile);
const storiesModule = server.moduleGraph.getModuleById(
getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)
);
if (storiesModule) {
server.moduleGraph.invalidateModule(storiesModule);
}
Expand All @@ -45,7 +44,7 @@ export function codeGeneratorPlugin(options: Options): Plugin {
// TODO maybe use the stories declaration in main
if (/\.stories\.([tj])sx?$/.test(path) || /\.mdx$/.test(path)) {
// We need to emit a change event to trigger HMR
server.watcher.emit('change', virtualStoriesFile);
server.watcher.emit('change', SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE);
}
});
},
Expand All @@ -69,34 +68,34 @@ export function codeGeneratorPlugin(options: Options): Plugin {
iframeId = `${config.root}/iframe.html`;
},
resolveId(source) {
if (source === virtualFileId) {
return `${virtualFileId}`;
if (source === SB_VIRTUAL_FILES.VIRTUAL_APP_FILE) {
return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_APP_FILE);
}
if (source === iframePath) {
return iframeId;
}
if (source === virtualStoriesFile) {
return `${virtualStoriesFile}`;
if (source === SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE) {
return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE);
}
if (source === virtualPreviewFile) {
return virtualPreviewFile;
if (source === SB_VIRTUAL_FILES.VIRTUAL_PREVIEW_FILE) {
return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_PREVIEW_FILE);
}
if (source === virtualAddonSetupFile) {
return `${virtualAddonSetupFile}`;
if (source === SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE) {
return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE);
}

return undefined;
},
async load(id, config) {
if (id === `${virtualStoriesFile}`) {
if (id === getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)) {
return generateImportFnScriptCode(options);
}

if (id === `${virtualAddonSetupFile}`) {
if (id === getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE)) {
return generateAddonSetupCode();
}

if (id === `${virtualFileId}`) {
if (id === getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_APP_FILE)) {
return generateModernIframeScriptCode(options, projectRoot);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ const cases = [
input: `import { Rain, Jour as Day, Nuit as Night, Sun } from "${packageName}"`,
output: `const { Rain, Jour: Day, Nuit: Night, Sun } = ${globals[packageName]}`,
},
{
globals,
packageName,
input: `import {
Rain,
Jour as Day,
Nuit as Night,
Sun
} from "${packageName}"`,
output: `const {
Rain,
Jour: Day,
Nuit: Night,
Sun
} = ${globals[packageName]}`,
},
{
globals,
packageName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ export async function externalGlobalsPlugin(externals: Record<string, string>) {

return {
code: src.toString(),
map: src.generateMap({
source: id,
hires: true,
}),
map: null,
};
},
} satisfies Plugin;
Expand Down
65 changes: 41 additions & 24 deletions code/builders/builder-vite/src/plugins/webpack-stats-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import type { BuilderStats } from 'storybook/internal/types';
import slash from 'slash';
import type { Plugin } from 'vite';

import {
SB_VIRTUAL_FILES,
getOriginalVirtualModuleId,
getResolvedVirtualModuleId,
} from '../virtual-file-names';

/*
* Reason, Module are copied from chromatic types
* https://github.com/chromaui/chromatic-cli/blob/145a5e295dde21042e96396c7e004f250d842182/bin-src/types.ts#L265-L276
Expand Down Expand Up @@ -35,12 +41,13 @@ function stripQueryParams(filePath: string): string {
/** We only care about user code, not node_modules, vite files, or (most) virtual files. */
function isUserCode(moduleName: string) {
return Boolean(
moduleName &&
!moduleName.startsWith('vite/') &&
!moduleName.startsWith('\x00') &&
!moduleName.startsWith('\u0000') &&
moduleName !== 'react/jsx-runtime' &&
!moduleName.match(/node_modules\//)
(moduleName &&
// keep Storybook's virtual files because they import the story files, so they are essential to the module graph
Object.values(SB_VIRTUAL_FILES).includes(getOriginalVirtualModuleId(moduleName))) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using a more descriptive variable name instead of 'moduleName'

(!moduleName.startsWith('vite/') &&
!moduleName.startsWith('\0') &&
moduleName !== 'react/jsx-runtime' &&
!moduleName.match(/node_modules\//))
);
}

Expand All @@ -53,6 +60,14 @@ export function pluginWebpackStats({ workingDir }: WebpackStatsPluginOptions): W
if (filename.startsWith('/virtual:')) {
return filename;
}
// ! Maintain backwards compatibility with the old virtual file names
// ! to ensure that the stats file doesn't change between the versions
// ! Turbosnap is also only compatible with the old virtual file names
// ! the old virtual file names did not start with the obligatory \0 character
if (Object.values(SB_VIRTUAL_FILES).includes(getOriginalVirtualModuleId(filename))) {
return getOriginalVirtualModuleId(filename);
}

// Otherwise, we need them in the format `./path/to/file.js`.
else {
const relativePath = relative(workingDir, stripQueryParams(filename));
Expand Down Expand Up @@ -82,25 +97,27 @@ export function pluginWebpackStats({ workingDir }: WebpackStatsPluginOptions): W
// We want this to run after the vite build plugins (https://vitejs.dev/guide/api-plugin.html#plugin-ordering)
enforce: 'post',
moduleParsed: function (mod) {
if (isUserCode(mod.id)) {
mod.importedIds
.concat(mod.dynamicallyImportedIds)
.filter((name) => isUserCode(name))
.forEach((depIdUnsafe) => {
const depId = normalize(depIdUnsafe);
if (statsMap.has(depId)) {
const m = statsMap.get(depId);
if (m) {
m.reasons = (m.reasons ?? [])
.concat(createReasons([mod.id]))
.filter((r) => r.moduleName !== depId);
statsMap.set(depId, m);
}
} else {
statsMap.set(depId, createStatsMapModule(depId, [mod.id]));
}
});
if (!isUserCode(mod.id)) {
return;
}
mod.importedIds
.concat(mod.dynamicallyImportedIds)
.filter((name) => isUserCode(name))
.forEach((depIdUnsafe) => {
const depId = normalize(depIdUnsafe);
if (!statsMap.has(depId)) {
statsMap.set(depId, createStatsMapModule(depId, [mod.id]));
return;
}
Comment on lines +114 to +117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider extracting this logic into a separate function for better maintainability

const m = statsMap.get(depId);
if (!m) {
return;
}
m.reasons = (m.reasons ?? [])
.concat(createReasons([mod.id]))
.filter((r) => r.moduleName !== depId);
statsMap.set(depId, m);
});
Comment on lines +106 to +126
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might not be obvious from the diff, but this just changes the flow to use early-return, no actual logic changed.

},

storybookGetStats() {
Expand Down
18 changes: 14 additions & 4 deletions code/builders/builder-vite/src/virtual-file-names.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
export const virtualFileId = '/virtual:/@storybook/builder-vite/vite-app.js';
export const virtualStoriesFile = '/virtual:/@storybook/builder-vite/storybook-stories.js';
export const virtualPreviewFile = '/virtual:/@storybook/builder-vite/preview-entry.js';
export const virtualAddonSetupFile = '/virtual:/@storybook/builder-vite/setup-addons.js';
export const SB_VIRTUAL_FILES = {
VIRTUAL_APP_FILE: '/virtual:/@storybook/builder-vite/vite-app.js',
VIRTUAL_STORIES_FILE: '/virtual:/@storybook/builder-vite/storybook-stories.js',
VIRTUAL_PREVIEW_FILE: '/virtual:/@storybook/builder-vite/preview-entry.js',
VIRTUAL_ADDON_SETUP_FILE: '/virtual:/@storybook/builder-vite/setup-addons.js',
};

export function getResolvedVirtualModuleId(virtualModuleId: string) {
return `\0${virtualModuleId}`;
}

export function getOriginalVirtualModuleId(resolvedVirtualModuleId: string) {
return resolvedVirtualModuleId.slice(1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function vueComponentMeta(tsconfigPath = 'tsconfig.json'): Promise<

// exclude stories, virtual modules and storybook internals
const exclude =
/\.stories\.(ts|tsx|js|jsx)$|^\/virtual:|^\/sb-preview\/|\.storybook\/.*\.(ts|js)$/;
/\.stories\.(ts|tsx|js|jsx)$|^\0\/virtual:|^\/virtual:|^\/sb-preview\/|\.storybook\/.*\.(ts|js)$/;
const include = /\.(vue|ts|js|tsx|jsx)$/;
const filter = createFilter(include, exclude);

Expand Down
Loading