diff --git a/code/lib/builder-manager/src/index.ts b/code/lib/builder-manager/src/index.ts index fb36d1e42638..10f4e5c5e209 100644 --- a/code/lib/builder-manager/src/index.ts +++ b/code/lib/builder-manager/src/index.ts @@ -23,8 +23,7 @@ import { getData } from './utils/data'; import { safeResolve } from './utils/safeResolve'; import { readOrderedFiles } from './utils/files'; -// eslint-disable-next-line import/no-mutable-exports -export let compilation: Compilation; +let compilation: Compilation; let asyncIterator: ReturnType | ReturnType; export const getConfig: ManagerBuilder['getConfig'] = async (options) => { @@ -128,7 +127,7 @@ const starter: StarterFunction = async function* starterGeneratorFn({ router.use(`/sb-addons`, express.static(addonsDir)); router.use(`/sb-manager`, express.static(coreDirOrigin)); - const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir); + const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles); yield; @@ -193,7 +192,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, yield; const managerFiles = copy(coreDirOrigin, coreDirTarget); - const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir); + const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles); yield; diff --git a/code/lib/builder-manager/src/utils/files.test.ts b/code/lib/builder-manager/src/utils/files.test.ts new file mode 100644 index 000000000000..41d62ec82693 --- /dev/null +++ b/code/lib/builder-manager/src/utils/files.test.ts @@ -0,0 +1,19 @@ +import { sanitizePath } from './files'; + +test('sanitizePath', () => { + const addonsDir = '/Users/username/Projects/projectname/storybook'; + const text = 'demo text'; + const file = { + path: '/Users/username/Projects/projectname/storybook/node_modules/@storybook/addon-x+y/dist/manager.mjs', + contents: Uint8Array.from(Array.from(text).map((letter) => letter.charCodeAt(0))), + text, + }; + const { location, url } = sanitizePath(file, addonsDir); + + expect(location).toMatchInlineSnapshot( + `"/Users/username/Projects/projectname/storybook/node_modules/@storybook/addon-x+y/dist/manager.mjs"` + ); + expect(url).toMatchInlineSnapshot( + `"./sb-addons/node_modules/%40storybook/addon-x%2By/dist/manager.mjs"` + ); +}); diff --git a/code/lib/builder-manager/src/utils/files.ts b/code/lib/builder-manager/src/utils/files.ts index 56c6204ead56..0b7fd8fedba0 100644 --- a/code/lib/builder-manager/src/utils/files.ts +++ b/code/lib/builder-manager/src/utils/files.ts @@ -1,11 +1,19 @@ +import { OutputFile } from 'esbuild'; import { writeFile, ensureFile } from 'fs-extra'; -import { compilation } from '../index'; +import { join } from 'path'; +import { Compilation } from '../types'; -export async function readOrderedFiles(addonsDir: string) { +export async function readOrderedFiles( + addonsDir: string, + outputFiles: Compilation['outputFiles'] | undefined +) { const files = await Promise.all( - compilation?.outputFiles?.map(async (file) => { - await ensureFile(file.path).then(() => writeFile(file.path, file.contents)); - return file.path.replace(addonsDir, './sb-addons'); + outputFiles?.map(async (file) => { + // convert deeply nested paths to a single level, also remove special characters + const { location, url } = sanitizePath(file, addonsDir); + + await ensureFile(location).then(() => writeFile(location, file.contents)); + return url; }) || [] ); @@ -13,3 +21,10 @@ export async function readOrderedFiles(addonsDir: string) { const cssFiles = files.filter((file) => file.endsWith('.css')); return { cssFiles, jsFiles }; } + +export function sanitizePath(file: OutputFile, addonsDir: string) { + const filePath = file.path.replace(addonsDir, ''); + const location = join(addonsDir, filePath); + const url = `./sb-addons${filePath.split('/').map(encodeURIComponent).join('/')}`; + return { location, url }; +}