Skip to content

Commit

Permalink
Use virtual module for the ladle vite plugin (#144)
Browse files Browse the repository at this point in the history
* Use virtual module for the ladle vite plugin

* Fix tests

* Fix sourcemaps in prod

* Changeset
  • Loading branch information
tajo committed May 25, 2022
1 parent 2e06dc6 commit daa717d
Show file tree
Hide file tree
Showing 17 changed files with 110 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-hairs-teach.md
@@ -0,0 +1,5 @@
---
"@ladle/react": patch
---

Fixing sourcemaps when using ladle build --sourcemap while using virtual module for our story list and metadata.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -32,6 +32,7 @@
"@commitlint/cli": "^16.2.4",
"@commitlint/config-conventional": "^16.2.4",
"@playwright/test": "1.19.2",
"@types/jest": "^27.4.1",
"@types/react": "^18.0.8",
"@types/react-dom": "^18.0.3",
"@typescript-eslint/eslint-plugin": "^5.21.0",
Expand Down
15 changes: 0 additions & 15 deletions packages/ladle/lib/app/generated/generated-list.tsx

This file was deleted.

4 changes: 0 additions & 4 deletions packages/ladle/lib/app/manifest.json
@@ -1,10 +1,6 @@
{
"name": "Ladle",
"short_name": "Ladle",
"icons": [{
"src": "touch-icon.png",
"sizes": "512x512"
}],
"background_color": "#ffffff",
"theme_color": "#ffffff",
"display": "fullscreen"
Expand Down
2 changes: 1 addition & 1 deletion packages/ladle/lib/app/src/addons/source.tsx
Expand Up @@ -3,7 +3,7 @@ import queryString from "query-string";
import Highlight, { defaultProps } from "prism-react-renderer";
import themeLight from "prism-react-renderer/themes/github";
import themeDark from "prism-react-renderer/themes/nightOwl";
import { storySource, stories } from "../../generated/generated-list";
import { storySource, stories } from "virtual:generated-list";
import { AddonProps, GlobalState, ActionType } from "../../../shared/types";
import { Source } from "../icons";
import { Modal, Code } from "../ui";
Expand Down
2 changes: 1 addition & 1 deletion packages/ladle/lib/app/src/app.tsx
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import {
stories as unsortedStories,
errorMessage,
} from "../generated/generated-list";
} from "virtual:generated-list";
import Story from "./story";
import NoStories from "./no-stories";
import NoStoriesError from "./no-stories-error";
Expand Down
9 changes: 4 additions & 5 deletions packages/ladle/lib/app/src/get-config.ts
@@ -1,17 +1,16 @@
import merge from "lodash.merge";
import { config as configAny, stories } from "../generated/generated-list";
import { config, stories } from "virtual:generated-list";
import defaultConfig from "../../shared/default-config";
import type { Config } from "../../shared/types";
import debug from "./debug";

const configTyped = configAny as Config;
if (Object.keys(configTyped).length === 0) {
if (Object.keys(config).length === 0) {
debug("No custom config found.");
} else {
debug(`Custom config found:`);
debug(configTyped);
debug(config);
}
const mergedConfig: Config = merge(defaultConfig, configTyped);
const mergedConfig: Config = merge(defaultConfig, config);
if (mergedConfig.defaultStory === "") {
mergedConfig.defaultStory = Object.keys(stories).sort()[0];
}
Expand Down
18 changes: 5 additions & 13 deletions packages/ladle/lib/app/src/story.tsx
@@ -1,33 +1,25 @@
import * as React from "react";
import ErrorBoundary from "./error-boundary";
import { stories, Provider } from "../generated/generated-list";
import { stories, Provider } from "virtual:generated-list";
import { Ring } from "./icons";
import type { GlobalState, GlobalAction } from "../../shared/types";
import config from "./get-config";
import NoStories from "./no-stories";

// wonky types because the mocked generated module can't have types
const ProviderAny = Provider as any;
const storiesAny = stories as any;

const Story: React.FC<{
globalState: GlobalState;
dispatch: React.Dispatch<GlobalAction>;
}> = ({ globalState, dispatch }) =>
globalState.story ? (
<ErrorBoundary>
<React.Suspense fallback={<Ring />}>
<ProviderAny
config={config}
globalState={globalState}
dispatch={dispatch}
>
{storiesAny[globalState.story] ? (
React.createElement(storiesAny[globalState.story].component)
<Provider config={config} globalState={globalState} dispatch={dispatch}>
{stories[globalState.story] ? (
React.createElement(stories[globalState.story].component)
) : (
<NoStories wrongUrl activeStory={globalState.story} />
)}
</ProviderAny>
</Provider>
</React.Suspense>
</ErrorBoundary>
) : null;
Expand Down
Expand Up @@ -42,7 +42,8 @@ const getComponents = (configFolder) => {
path.join(configFolder, "components.js"),
];
const firstFoundComponentsPath = componentsPaths.find((componentsPath) =>
fs.existsSync(componentsPath));
fs.existsSync(componentsPath),
);

if (!firstFoundComponentsPath) {
debug(`Returning default no-op Provider.`);
Expand All @@ -54,10 +55,12 @@ const getComponents = (configFolder) => {

firstFoundComponentsPath && debug(`${configFolder}/${filename} found.`);

const componentsRelativePath = path.relative(
path.join(__dirname, "../../../app/src"),
path.join(configFolder, filename),
);
const componentsRelativePath = path
.relative(
path.join(__dirname, "../../../app/src"),
path.join(configFolder, filename),
)
.slice(2);
if (checkIfNamedExportExists("Provider", sourceCode, filename)) {
debug(`Custom provider found.`);
return `import {Provider as CustomProvider} from '${componentsRelativePath}';\nexport const Provider = CustomProvider;\n`;
Expand Down
10 changes: 6 additions & 4 deletions packages/ladle/lib/cli/vite-plugin/generate/get-config-import.js
Expand Up @@ -12,10 +12,12 @@ const getConfigImport = (configFolder) => {
const configExists = fs.existsSync(configPath);
let configCode = `export let config = {};\n`;
if (configExists) {
configCode += `import customConfig from '${path.relative(
path.join(__dirname, "../../../app/src"),
path.join(configFolder, "config.mjs"),
)}';\nconfig = customConfig;\n`;
configCode += `import customConfig from '${path
.relative(
path.join(__dirname, "../../../app/src"),
path.join(configFolder, "config.mjs"),
)
.slice(2)}';\nconfig = customConfig;\n`;
}
return `${configCode}`;
};
Expand Down
12 changes: 7 additions & 5 deletions packages/ladle/lib/cli/vite-plugin/generate/get-story-imports.js
Expand Up @@ -11,7 +11,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
*/
const getStoryImports = (entryData) => {
let storyImports = `import { lazy, createElement, Fragment } from "react";\n`;
storyImports += `import composeEnhancers from "../src/compose-enhancers";\n`;
storyImports += `import composeEnhancers from "/src/compose-enhancers";\n`;
const lazyImport = /** @type {any} */ (template).default(`
const %%component%% = lazy(() =>
import(%%source%%).then((module) => {
Expand All @@ -24,10 +24,12 @@ const getStoryImports = (entryData) => {
entryData[entry].stories.forEach(({ componentName, namedExport }) => {
const ast = lazyImport({
source: t.stringLiteral(
path.relative(
path.join(__dirname, "../../../app/src"),
path.join(process.cwd(), entry),
),
path
.relative(
path.join(__dirname, "../../../app/src"),
path.join(process.cwd(), entry),
)
.slice(2),
),
component: t.identifier(componentName),
story: t.stringLiteral(namedExport),
Expand Down
Expand Up @@ -32,7 +32,6 @@ const getDefaultExport = (result, astPath) => {
}
});
} catch (e) {
console.log(e);
throw new Error(
`Can't parse the default title and meta of ${result.entry}. Meta must be serializable and title a string literal.`,
);
Expand Down
25 changes: 13 additions & 12 deletions packages/ladle/lib/cli/vite-plugin/vite-plugin.js
Expand Up @@ -16,14 +16,11 @@ const debug = debugFactory("ladle:vite");
const defaultListModule = (errorMessage) => `
import { lazy } from "react";
import * as React from "react";
export const Foo = lazy(() => Promise.resolve() as any);
export const list = ["Foo"];
export const list = [];
export const config = {};
export const stories = {};
export const storySource = {};
export const errorMessage = \`${errorMessage}\`;
export const Provider = ({ children }: { children: any }) =>
/*#__PURE__*/ React.createElement(React.Fragment, null, children);
`;
Expand All @@ -34,15 +31,16 @@ export const Provider = ({ children }: { children: any }) =>
* @param mode {string}
*/
function ladlePlugin(config, configFolder, mode) {
const virtualFileId = "lib/app/generated/generated-list";
const virtualModuleId = "virtual:generated-list";
const resolvedVirtualModuleId = "\0" + virtualModuleId;
return {
name: "generated-list", // required, will show up in warnings and errors
name: "ladle-plugin",
/**
* @param {string} id
*/
resolveId(id) {
if (id.includes(virtualFileId)) {
return virtualFileId;
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
return null;
},
Expand All @@ -68,19 +66,22 @@ function ladlePlugin(config, configFolder, mode) {
});
}`
: "";
return `${code}\n${invalidateHmr}\n${watcherImport}\nif (import.meta.hot) {
return {
code: `${code}\n${invalidateHmr}\n${watcherImport}\nif (import.meta.hot) {
import.meta.hot.accept(() => {
storyUpdated();
});
}`;
}`,
map: null,
};
}
return code;
return { code, map: null };
},
/**
* @param {string} id
*/
async load(id) {
if (id.includes(virtualFileId)) {
if (id === resolvedVirtualModuleId) {
debug(`transforming: ${id}`);
try {
debug("Initial generation of the list");
Expand Down
42 changes: 21 additions & 21 deletions packages/ladle/tests/__snapshots__/get-generated-list.test.ts.snap
Expand Up @@ -3,14 +3,14 @@
exports[`Capital letters in story names converted into delimiters 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const capitalization$$blue$tiny$cat = lazy(() => import(\\"../../../tests/fixtures/capitalization.stories.tsx\\").then(module => {
const capitalization$$blue$tiny$cat = lazy(() => import(\\"/../../tests/fixtures/capitalization.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"BlueTinyCat\\")
};
}));
const capitalization$$big$barking$dog = lazy(() => import(\\"../../../tests/fixtures/capitalization.stories.tsx\\").then(module => {
const capitalization$$big$barking$dog = lazy(() => import(\\"/../../tests/fixtures/capitalization.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"BigBarkingDog\\")
};
Expand Down Expand Up @@ -49,9 +49,9 @@ export const errorMessage = '';
exports[`Capital letters in the filename converted into delimiters 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const filename$capitalization$$test = lazy(() => import(\\"../../../tests/fixtures/filenameCapitalization.stories.tsx\\").then(module => {
const filename$capitalization$$test = lazy(() => import(\\"/../../tests/fixtures/filenameCapitalization.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Test\\")
};
Expand Down Expand Up @@ -83,9 +83,9 @@ export const errorMessage = '';
exports[`Default title is used instead of the file name 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const title$$cat = lazy(() => import(\\"../../../tests/fixtures/default-title.stories.tsx\\").then(module => {
const title$$cat = lazy(() => import(\\"/../../tests/fixtures/default-title.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Cat\\")
};
Expand Down Expand Up @@ -117,14 +117,14 @@ export const errorMessage = '';
exports[`Extract and merge story meta 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const title$$cat = lazy(() => import(\\"../../../tests/fixtures/story-meta.stories.tsx\\").then(module => {
const title$$cat = lazy(() => import(\\"/../../tests/fixtures/story-meta.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Cat\\")
};
}));
const title$$dog = lazy(() => import(\\"../../../tests/fixtures/story-meta.stories.tsx\\").then(module => {
const title$$dog = lazy(() => import(\\"/../../tests/fixtures/story-meta.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Dog\\")
};
Expand Down Expand Up @@ -183,9 +183,9 @@ export const errorMessage = '';
exports[`Extract default meta 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const title$$cat = lazy(() => import(\\"../../../tests/fixtures/default-meta.stories.tsx\\").then(module => {
const title$$cat = lazy(() => import(\\"/../../tests/fixtures/default-meta.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Cat\\")
};
Expand Down Expand Up @@ -225,14 +225,14 @@ export const errorMessage = '';
exports[`Single file with two stories 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const animals$$cat = lazy(() => import(\\"../../../tests/fixtures/animals.stories.tsx\\").then(module => {
const animals$$cat = lazy(() => import(\\"/../../tests/fixtures/animals.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Cat\\")
};
}));
const animals$$dog = lazy(() => import(\\"../../../tests/fixtures/animals.stories.tsx\\").then(module => {
const animals$$dog = lazy(() => import(\\"/../../tests/fixtures/animals.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Dog\\")
};
Expand Down Expand Up @@ -271,19 +271,19 @@ export const errorMessage = '';
exports[`Story name replaces named export as a story name 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const storyname$$doggo = lazy(() => import(\\"../../../tests/fixtures/storyname.stories.tsx\\").then(module => {
const storyname$$doggo = lazy(() => import(\\"/../../tests/fixtures/storyname.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Cat\\")
};
}));
const storyname$$capital$city = lazy(() => import(\\"../../../tests/fixtures/storyname.stories.tsx\\").then(module => {
const storyname$$capital$city = lazy(() => import(\\"/../../tests/fixtures/storyname.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"CapitalCity\\")
};
}));
const storyname$$champs$élysées = lazy(() => import(\\"../../../tests/fixtures/storyname.stories.tsx\\").then(module => {
const storyname$$champs$élysées = lazy(() => import(\\"/../../tests/fixtures/storyname.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"CapitalReplaced\\")
};
Expand Down Expand Up @@ -329,9 +329,9 @@ export const errorMessage = '';
exports[`Turn file name delimiters into spaces and levels correctly 1`] = `
"
import { lazy, createElement, Fragment } from \\"react\\";
import composeEnhancers from \\"../src/compose-enhancers\\";
import composeEnhancers from \\"/src/compose-enhancers\\";
const our$animals$$mammals$$cat = lazy(() => import(\\"../../../tests/fixtures/our-animals--mammals.stories.tsx\\").then(module => {
const our$animals$$mammals$$cat = lazy(() => import(\\"/../../tests/fixtures/our-animals--mammals.stories.tsx\\").then(module => {
return {
default: composeEnhancers(module, \\"Cat\\")
};
Expand Down

1 comment on commit daa717d

@vercel
Copy link

@vercel vercel bot commented on daa717d May 25, 2022

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

ladle – ./

ladle-git-master-miksu.vercel.app
ladle.vercel.app
ladle-miksu.vercel.app
ladle.dev

Please sign in to comment.