Skip to content

Commit 024bbea

Browse files
committed
some changes
1 parent 4ebb1f8 commit 024bbea

File tree

7 files changed

+174
-189
lines changed

7 files changed

+174
-189
lines changed

package-lock.json

Lines changed: 8 additions & 85 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/generators/web/build/bundle.mjs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { writeFile } from 'node:fs/promises';
2+
3+
import esbuild from 'esbuild';
4+
5+
import { ESBUILD_RESOLVE_DIR, POLYFILL_PATH } from '../constants.mjs';
6+
import getPlugins from './plugins.mjs';
7+
import staticData from '../server/data.mjs';
8+
9+
/** @typedef {{ server: boolean, debug: boolean }} BundleOptions */
10+
11+
/**
12+
* Creates the esbuild configuration object
13+
* @param {string} code - Source code to bundle
14+
* @param {BundleOptions} options - Options
15+
* @returns {import('esbuild').BuildOptions} ESBuild configuration
16+
*/
17+
const createConfig = (code, { server, debug }) => ({
18+
stdin: {
19+
contents: code,
20+
resolveDir: ESBUILD_RESOLVE_DIR,
21+
loader: 'jsx',
22+
},
23+
bundle: true,
24+
minify: true,
25+
format: 'iife',
26+
platform: server ? 'node' : 'browser',
27+
jsx: 'automatic',
28+
write: false,
29+
outfile: 'output.js',
30+
// When updating the `define` object,
31+
// also update client/types.d.ts to
32+
// include the newly defined globals
33+
define: {
34+
// Inject static data at build time
35+
__STATIC_DATA__: staticData,
36+
// Use if(CLIENT) or if(SERVER)
37+
// in order to only run code in
38+
// the specific environment, and
39+
// drop it otherwise.
40+
SERVER: String(server),
41+
CLIENT: String(!server),
42+
},
43+
alias: {
44+
react: 'preact/compat',
45+
'react-dom': 'preact/compat',
46+
// TODO(@avivkeller): Orama _currently_ doesn't support server-side
47+
// rendering, but that is supposed to change in the near future.
48+
...(server && { '@orama/react-components': POLYFILL_PATH }),
49+
},
50+
plugins: getPlugins(server),
51+
metafile: debug,
52+
});
53+
54+
/**
55+
* Bundles JavaScript code and returns JS/CSS content
56+
* @param {string} code - Source code to bundle
57+
* @param {BundleOptions} options - Options
58+
* @returns {Promise<{js: string, css?: string}>}
59+
*/
60+
export default async (code, { server = false, debug = true } = {}) => {
61+
const config = createConfig(code, { server, debug });
62+
const result = await esbuild.build(config);
63+
const [jsFile, cssFile] = result.outputFiles;
64+
65+
if (debug) {
66+
await writeFile(
67+
`out/meta-${server ? 'server' : 'client'}.json`,
68+
JSON.stringify(result.metafile)
69+
);
70+
}
71+
72+
return {
73+
js: jsFile.text,
74+
css: cssFile?.text,
75+
};
76+
};

src/generators/web/build/plugins.mjs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { existsSync } from 'node:fs';
2+
import { fileURLToPath } from 'node:url';
3+
4+
import tailwindcss from '@tailwindcss/postcss';
5+
import stylePlugin from 'esbuild-style-plugin';
6+
import postcssCalc from 'postcss-calc';
7+
8+
/**
9+
* ESBuild plugin that resolves UI component imports
10+
*
11+
* This plugin intercepts imports starting with '@node-core/ui-components' or '#ui/'
12+
* and resolves them to the corresponding TypeScript files. It follows a resolution
13+
* strategy where it first tries to find an index.tsx file, then falls back to a
14+
* .tsx file with the same name.
15+
* ```
16+
*/
17+
export const uiComponentsResolverPlugin = {
18+
name: 'ui-components-resolver',
19+
20+
/**
21+
* @param {import('esbuild').PluginBuild} build
22+
*/
23+
setup(build) {
24+
build.onResolve(
25+
{ filter: /^(@node-core\/ui-components|#ui\/)/ },
26+
({ path }) => {
27+
// Skip paths that already have file extensions
28+
if (/\.[a-zA-Z0-9]+$/.test(path)) return;
29+
30+
// Normalize the import path by converting #ui/ alias to the full package name
31+
const normalizedPath = path.replace(
32+
/^#ui\//,
33+
'@node-core/ui-components/'
34+
);
35+
36+
// Resolution strategy: try index.tsx first, then .tsx
37+
const resolutionSuffixes = ['/index.tsx', '.tsx'];
38+
39+
for (const suffix of resolutionSuffixes) {
40+
try {
41+
const resolvedPath = fileURLToPath(
42+
import.meta.resolve(normalizedPath + suffix)
43+
);
44+
45+
if (existsSync(resolvedPath)) {
46+
return { path: resolvedPath };
47+
}
48+
} catch {
49+
// Silently ignore resolution errors and try the next suffix
50+
continue;
51+
}
52+
}
53+
54+
// If no resolution found, let ESBuild handle it with default behavior
55+
return undefined;
56+
}
57+
);
58+
},
59+
};
60+
61+
/**
62+
* Creates and returns an array of ESBuild plugins for the build process
63+
*
64+
* @param {boolean} server - Whether this is running in server mode.
65+
*/
66+
export default server => {
67+
const plugins = [
68+
uiComponentsResolverPlugin,
69+
stylePlugin(
70+
// We still need to include `stylePlugin` on the server, so that
71+
// the hydrated HTML will have the class names we need, however,
72+
// we don't need to parse them through PostCSS for performance.
73+
server
74+
? {}
75+
: {
76+
postcss: {
77+
plugins: [tailwindcss(), postcssCalc()],
78+
},
79+
}
80+
),
81+
];
82+
83+
return plugins;
84+
};

src/generators/web/client/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ import { createStaticData } from '../server/data.mjs';
22

33
declare global {
44
const __STATIC_DATA__: ReturnType<typeof createStaticData>;
5+
const SERVER: boolean;
6+
const CLIENT: boolean;
57
}

src/generators/web/index.mjs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { estreeToBabel } from 'estree-to-babel';
66
import { minify } from 'html-minifier-terser';
77
import Mustache from 'mustache';
88

9-
import { ESBUILD_RESOLVE_DIR, POLYFILL_PATH } from './constants.mjs';
9+
import { ESBUILD_RESOLVE_DIR } from './constants.mjs';
1010
import { TERSER_MINIFY_OPTIONS } from '../../constants.mjs';
11-
import createASTBuilder from './utils/astBuilder.mjs';
12-
import bundleCode from './utils/bundle.mjs';
11+
import bundleCode from './build/bundle.mjs';
12+
import createASTBuilder from './build/generate.mjs';
1313

1414
/**
1515
* Executes server-side code in a safe, isolated context
@@ -18,13 +18,7 @@ import bundleCode from './utils/bundle.mjs';
1818
* @returns {Promise<string>} The rendered HTML output
1919
*/
2020
async function executeServerCode(serverCode, require) {
21-
const { js: bundledServer } = await bundleCode(serverCode, {
22-
platform: 'node',
23-
alias: {
24-
// These can ONLY be loaded on the client
25-
'@orama/react-components': POLYFILL_PATH,
26-
},
27-
});
21+
const { js: bundledServer } = await bundleCode(serverCode, { server: true });
2822

2923
const executedFunction = new Function(
3024
'require',

0 commit comments

Comments
 (0)