Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/php-wasm/node/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ export default defineConfig(function () {
},
sourcemap: true,
rollupOptions: {
// Don't bundle the PHP loaders in the final build. See
// the preserve-php-loaders-imports plugin above.
external: getExternalModules(),
output: {
entryFileNames: '[name].js',
Expand Down
203 changes: 77 additions & 126 deletions packages/php-wasm/web/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,144 +8,95 @@ import { viteTsConfigPaths } from '../../vite-extensions/vite-ts-config-paths';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { viteIgnoreImports } from '../../vite-extensions/vite-ignore-imports';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { vitePreserveLoadersImports } from '../../vite-extensions/vite-preserve-loaders-imports';
// eslint-disable-next-line @nx/enforce-module-boundaries
import viteGlobalExtensions from '../../vite-extensions/vite-global-extensions';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { getExternalModules } from '../../vite-extensions/vite-external-modules';

export default defineConfig(({ command }) => {
return {
cacheDir: '../../../node_modules/.vite/php-wasm',
export default defineConfig({
cacheDir: '../../../node_modules/.vite/php-wasm',

plugins: [
viteTsConfigPaths({
root: '../../../',
}),
dts({
entryRoot: 'src',
tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'),
pathsToAliases: false,
}),
viteIgnoreImports({
extensions: ['wasm', 'so', 'dat'],
}),
/**
* Vite can't extract static asset in the library mode:
* https://github.com/vitejs/vite/issues/3295
*
* This workaround replaces the actual php_5_6.js modules paths used
* in the dev mode with their filenames. Then, the filenames are marked
* as external further down in this config. As a result, the final
* bundle contains literal `import('php_5_6.js')` and
* `import('php_5_6.wasm')` statements which allows the consumers to use
* their own loaders.
*
* This keeps the dev mode working AND avoids inlining 5mb of
* wasm via base64 in the final bundle.
*/
plugins: [
viteTsConfigPaths({
root: '../../../',
}),
dts({
entryRoot: 'src',
tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'),
pathsToAliases: false,
}),
viteIgnoreImports({
extensions: ['wasm', 'so', 'dat'],
}),
vitePreserveLoadersImports([
{
name: 'preserve-php-loaders-imports',

resolveDynamicImport(specifier): string | void {
if (
command === 'build' &&
typeof specifier === 'string' &&
specifier.match(/php_\d_\d\.js$/)
) {
/**
* The ../ is weird but necessary to make the final build say
* import("./php/jspi/php_8_2.js")
* and not
* import("php/jspi/php_8_2.js")
*
* The slice(-3) will ensure the 'php/jspi/'
* portion of the path is preserved.
*/
return '../' + specifier.split('/').slice(-3).join('/');
}
},
regex: /php_\d_\d\.js$/,
/*
* ../ lifts the import located file up to the dist entryRoot
* web/src/lib/get-php-loader-module.ts > web/src/get-php-loader-module.ts
*
* slice(-3) strips the `public` directory from the path
* web/public/php/jspi/php_8_4.js > ./web/php/jspi/php_8_4.js
*/
transform: (specifier) =>
`../${specifier.split('/').slice(-3).join('/')}`,
},
{
name: 'preserve-data-loaders-imports',

resolveDynamicImport(specifier): string | void {
if (
command === 'build' &&
typeof specifier === 'string' &&
specifier.match(/icu\.dat$/)
) {
/**
* The ../../../ is weird but necessary to make the final build say
* import("./shared/icu.dat")
* and not
* import("shared/icu.dat")
*
* The slice(-2) will ensure the 'shared/'
* portion of the path is preserved.
*/
return (
'../../../' +
specifier.split('/').slice(-2).join('/')
);
}
},
regex: /intl\.so$/,
/*
* ../../../ lifts the import located file to the dist entryRoot
* web/src/lib/extensions/intl/get-intl-loader-module.ts > web/src/get-intl-loader-module.ts
*
* slice(-6) strips the `public` directory from the path
* web/public/php/jspi/extensions/8_4/intl/intl.so > ./web/php/jspi/extensions/8_4/intl/intl.so
*/
transform: (specifier) =>
`../../../${specifier.split('/').slice(-6).join('/')}`,
},
{
name: 'preserve-extension-loaders-imports',

resolveDynamicImport(specifier): string | void {
if (
command === 'build' &&
typeof specifier === 'string' &&
specifier.match(/intl\.so$/)
) {
/**
* The ../../../ is weird but necessary to make the final build say
* import("./php/{mode}/extensions/intl/{php_version}/intl.so")
* and not
* import("php/{mode}/extensions/intl/{php_version}/intl.so")
*
* The slice(-6) will ensure the 'php/{mode}/extensions/intl/{php_version}'
* portion of the path is preserved.
*/
return (
'../../../' +
specifier.split('/').slice(-6).join('/')
);
}
},
regex: /icu\.dat$/,
/*
* ../../../ lifts the import located file to the dist entryRoot
* web/src/lib/extensions/intl/with-intl.ts > web/src/with-intl.ts
*
* slice(-2) strips the `public` directory from the path
* web/public/shared/icu.dat > ./web/shared/icu.dat
*/
transform: (specifier) =>
`../../../${specifier.split('/').slice(-2).join('/')}`,
},
]),
...viteGlobalExtensions,
],

...viteGlobalExtensions,
],

// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: 'src/index.ts',
name: 'php-wasm-web',
fileName: 'index',
formats: ['es', 'cjs'],
},
sourcemap: true,
rollupOptions: {
// Don't bundle the PHP loaders in the final build. See
// the preserve-php-loaders-imports plugin above.
external: [
/php_\d_\d.js$/,
/icu.dat$/,
/intl.so$/,
...getExternalModules(),
],
},
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: 'src/index.ts',
name: 'php-wasm-web',
fileName: 'index',
formats: ['es', 'cjs'],
},

// TODO : move Vitest tests to Playwright tests inside test directory
test: {
globals: true,
environment: 'node',
reporters: ['default'],
sourcemap: true,
rollupOptions: {
// Don't bundle the PHP loaders in the final build. See
// the preserve-php-loaders-imports plugin above.
external: [
/php_\d_\d.js$/,
/icu.dat$/,
/intl.so$/,
...getExternalModules(),
],
},
};
},

// TODO : move Vitest tests to Playwright tests inside test directory
test: {
globals: true,
environment: 'node',
reporters: ['default'],
},
});
4 changes: 2 additions & 2 deletions packages/php-wasm/web/vite.playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { defineConfig, mergeConfig } from 'vite';
import config from './vite.config';

export default defineConfig((env) =>
export default defineConfig(() =>
mergeConfig(
config(env),
config,
defineConfig({
assetsInclude: ['**/*.wasm', '**/*.so', '**/*.dat'],

Expand Down
78 changes: 78 additions & 0 deletions packages/vite-extensions/vite-preserve-loaders-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type { Plugin } from 'vite';

export interface PreserveLoadersRule {
regex: RegExp;
transform: (specifier: string) => string;
}

export function vitePreserveLoadersImports(
rules: PreserveLoadersRule[]
): Plugin {
let command: 'build' | 'serve';

const matchedRules = new Set<PreserveLoadersRule>();

return {
/**
* Vite can't extract static asset in the library mode:
* https://github.com/vitejs/vite/issues/3295
*
* This workaround replaces the actual php.js modules paths used
* in the dev mode with their filenames. Then, the filenames are marked
* as external further down in this config. As a result, the final
* bundle contains literal `import('php.js')` and
* `import('php.wasm')` statements which allows the consumers to use
* their own loaders.
*
* This keeps the dev mode working AND avoids inlining 5mb of
* wasm via base64 in the final bundle.
*/
name: 'vite-preserve-loaders-imports',

configResolved(config) {
command = config.command;
},

resolveDynamicImport(specifier) {
if (command !== 'build' || typeof specifier !== 'string') return;

for (const rule of rules) {
if (new RegExp(rule.regex).test(specifier)) {
matchedRules.add(rule);
/**
*
* Example : transform: specifier => `../${specifier.split('/').slice(-3).join('/')}`
*
* The '../' is weird but necessary to make the final build say
* import("./php/jspi/php.js")
* and not
* import("php/jspi/php.js")
*
* The -3 will ensure the 'php/jspi/'
* portion of the path is preserved.
*/
return rule.transform(specifier);
}
}

return null;
},

buildEnd() {
if (command !== 'build') return;

const unusedRules = rules.filter((rule) => !matchedRules.has(rule));

if (unusedRules.length > 0) {
const details = unusedRules
.map((rule) => `- ${rule.regex}`)
.join('\n');

this.error(
`vite-preserve-loaders-imports: The following rules did not match any dynamic imports:\n${details}\n\n` +
`This is likely a misconfiguration or a stale regex.`
);
}
},
};
}