Skip to content

Commit

Permalink
add support for importing css files in fixture
Browse files Browse the repository at this point in the history
  • Loading branch information
nksaraf committed Jun 11, 2023
1 parent d5a7af9 commit a8492b6
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 1 deletion.
5 changes: 5 additions & 0 deletions fixtures/flight-vite/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ async function build() {
external: ['react', 'react-dom', 'react-server-dom-vite'],
},
});

// copy assets from react-server build to static build, this includes stylesheets improted from server components
await fs.promises.cp('build/react-server/assets', 'build/static/assets', {
recursive: true,
});
}

build();
52 changes: 52 additions & 0 deletions fixtures/flight-vite/server/manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

/**
* Traverses the module graph and collects assets for a given chunk
*
* @param manifest Client manifest
* @param id Chunk id
* @param assetMap Cache of assets
* @returns Array of asset URLs
*/
const findAssetsInManifest = (manifest, id, assetMap = new Map()) => {
function traverse(id) {
const cached = assetMap.get(id);
if (cached) {
return cached;
}
const chunk = manifest[id];
if (!chunk) {
return [];
}
const assets = [
...(chunk.assets || []),
...(chunk.css || []),
...(chunk.imports?.flatMap(traverse) || []),
];
const imports = chunk.imports?.flatMap(traverse) || [];
const all = [...assets, ...imports].filter(Boolean);
all.push(chunk.file);
assetMap.set(id, all);
return Array.from(new Set(all));
}
return traverse(id);
};

const findAssetsInModuleNode = moduleNode => {
const seen = new Set();
function traverse(node) {
if (seen.has(node.url)) {
return [];
}
seen.add(node.url);

const imports = [...node.importedModules].flatMap(traverse) || [];
imports.push(node.url);
return Array.from(new Set(imports));
}
return traverse(moduleNode);
};

module.exports = {
findAssetsInManifest,
};
13 changes: 13 additions & 0 deletions fixtures/flight-vite/server/region.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ async function createApp() {
return viteServer.ssrLoadModule(id);
};

const {collectStyles} = require('./styles.js');
globalThis.__vite_find_assets__ = async entries => {
return Object.keys(await collectStyles(viteServer, entries));
};

loadModule = async entry => {
return await viteServer.ssrLoadModule(
path.isAbsolute(entry)
Expand Down Expand Up @@ -92,10 +97,18 @@ async function createApp() {

globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_require__ = id => {
console.log({id});
return import(
path.join(process.cwd(), 'build', 'react-server', id + '.js')
);
};
const {findAssetsInManifest} = require('./manifest.js');

globalThis.__vite_find_assets__ = async entries => {
return findAssetsInManifest(reactServerManifest, entries[0]).filter(
asset => asset.endsWith('.css')
);
};
}

async function renderApp(res, returnValue) {
Expand Down
103 changes: 103 additions & 0 deletions fixtures/flight-vite/server/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
'use strict';

const path = require('node:path');

async function findDeps(vite, node, deps) {
// since `ssrTransformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
// instead of using `await`, we resolve all branches in parallel.
const branches = [];

async function add(node) {
if (!deps.has(node)) {
deps.add(node);
await findDeps(vite, node, deps);
}
}

async function add_by_url(url) {
const node = await vite.moduleGraph.getModuleByUrl(url);

if (node) {
await add(node);
}
}

if (node.ssrTransformResult) {
if (node.ssrTransformResult.deps) {
node.ssrTransformResult.deps.forEach(url =>
branches.push(add_by_url(url))
);
}

// if (node.ssrTransformResult.dynamicDeps) {
// node.ssrTransformResult.dynamicDeps.forEach(url => branches.push(add_by_url(url)));
// }
} else {
node.importedModules.forEach(node => branches.push(add(node)));
}

await Promise.all(branches);
}

// Vite doesn't expose this so we just copy the list for now
// https://github.com/vitejs/vite/blob/3edd1af56e980aef56641a5a51cf2932bb580d41/packages/vite/src/node/plugins/css.ts#L96
const STYLE_ASSET_REGEX = /\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/;
const MODULE_STYLE_ASSET_REGEX =
/\.module\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/;

async function collectStyles(devServer, match) {
const styles = {};
const deps = new Set();
try {
for (const file of match) {
const resolvedId = await devServer.pluginContainer.resolveId(file);

if (!resolvedId) {
console.log('not found');
continue;
}

const id = resolvedId.id;

const normalizedPath = path.resolve(id).replace(/\\/g, '/');
let node = devServer.moduleGraph.getModuleById(normalizedPath);
if (!node) {
const absolutePath = path.resolve(file);
await devServer.ssrLoadModule(absolutePath);
node = await devServer.moduleGraph.getModuleByUrl(absolutePath);

if (!node) {
console.log('not found');
return;
}
}

await findDeps(devServer, node, deps);
}
} catch (e) {
console.error(e);
}

for (const dep of deps) {
const parsed = new URL(dep.url, 'http://localhost/');
const query = parsed.searchParams;

if (STYLE_ASSET_REGEX.test(dep.file ?? '')) {
try {
const mod = await devServer.ssrLoadModule(dep.url);
// if (module_STYLE_ASSET_REGEX.test(dep.file)) {
// styles[dep.url] = env.cssModules?.[dep.file];
// } else {
styles[dep.url] = mod.default;
// }
} catch {
// this can happen with dynamically imported modules, I think
// because the Vite module graph doesn't distinguish between
// static and dynamic imports? TODO investigate, submit fix
}
}
}
return styles;
}

module.exports = {collectStyles};
13 changes: 12 additions & 1 deletion fixtures/flight-vite/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ const REACT_REFRESH_PREAMBLE = `
window.__vite_plugin_react_preamble_installed__ = true
`;

async function Assets() {
const styles = await __vite_find_assets__(['src/App.jsx']);
return (
<>
{styles.map(key => (
<link key={key} rel="stylesheet" href={key} />
))}
</>
);
}

export default async function App() {
const res = await fetch('http://localhost:3001/todos');
const todos = await res.json();
Expand All @@ -23,7 +34,6 @@ export default async function App() {
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Flight</title>
<link rel="stylesheet" href="/src/style.css" />
{import.meta.env.DEV ? (
<>
<script type="module" src="@vite/client" />
Expand All @@ -35,6 +45,7 @@ export default async function App() {
/>
</>
) : null}
<Assets />
</head>
<body>
<div>
Expand Down

0 comments on commit a8492b6

Please sign in to comment.