Skip to content

Commit 6ea85ce

Browse files
authored
support nodejs esm (#68)
* support nodejs esm env * fix ssr in node esm env * chore: publish alpha * WIP: tests * fix import client and clientTypes * update lock * WIP: tests * tests cleanup * fix test: kill ESRCH * improve tests * improve test setup * add comment * cleanup * improve test timeout setup
1 parent d8e247e commit 6ea85ce

File tree

18 files changed

+1127
-96
lines changed

18 files changed

+1127
-96
lines changed

packages/playground/use-theme-doc/__tests__/navigate-tests.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
import { test, expect, type Page } from '~utils'
1+
import { test as baseTest, expect, type Page } from '~utils'
22
import { userPages } from './snapshots'
33

44
// reuse test declaration
5-
export function declareTests(javaScriptEnabled: boolean) {
5+
export function declareTests({
6+
javaScriptEnabled = true,
7+
test = baseTest,
8+
}: {
9+
javaScriptEnabled?: boolean
10+
test?: typeof baseTest
11+
} = {}) {
612
test('test options', async ({ javaScriptEnabled: javaScriptEnabledOpt }) => {
713
await expect(javaScriptEnabledOpt).toBe(javaScriptEnabled)
814
})
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { test } from '~utils'
2+
import { declareTests } from './navigate-tests'
3+
4+
test.use({
5+
beforeStartViteServer: async ({ fsUtils }, use) => {
6+
await use(async () => {
7+
fsUtils.editFile('package.json', (str: string) => {
8+
if (!str.includes('"type": "module"'))
9+
throw new Error(
10+
'unexpected package.json:should includes type:module'
11+
)
12+
return str.replace('"type": "module"', '"type": "commonjs"')
13+
})
14+
})
15+
},
16+
})
17+
18+
declareTests()
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
import { test } from '~utils'
12
import { declareTests } from './navigate-tests'
23

3-
declareTests(false)
4+
test.use({ javaScriptEnabled: false })
5+
6+
test.skip(
7+
({ vitePagesMode }) => vitePagesMode !== 'ssr',
8+
'only run in ssr mode'
9+
)
10+
11+
test.use({
12+
skipPrepare: async ({ vitePagesMode }, use) => {
13+
await use(vitePagesMode !== 'ssr')
14+
},
15+
})
16+
17+
declareTests({ javaScriptEnabled: false })
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import { declareTests } from './navigate-tests'
22

3-
declareTests(true)
3+
declareTests()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "commonjs",
3+
"//": "needed for playwright to work (../package.json has set type to module)"
4+
}

packages/playground/use-theme-doc/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "use-theme",
33
"version": "1.0.0",
44
"private": true,
5+
"type": "module",
56
"scripts": {
67
"dev": "vite serve",
78
"debug": "node --inspect ./node_modules/vite/bin/vite serve",

packages/playground/use-theme-doc/vite.config.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { defineConfig } from 'vite'
22
import * as path from 'path'
33
import react from '@vitejs/plugin-react'
4-
import mdx from 'vite-plugin-mdx'
4+
import _mdx from 'vite-plugin-mdx'
55
import pages from 'vite-plugin-react-pages'
66

7-
module.exports = defineConfig({
7+
// vite-plugin-mdx is not esm friendly currently
8+
const mdx = ((_mdx as any).default || _mdx) as typeof _mdx
9+
10+
export default defineConfig({
811
resolve: {
912
alias: {
1013
'~pages/': `${path.join(__dirname, 'pages')}/`,

packages/react-pages/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
"name": "vite-plugin-react-pages",
33
"version": "3.2.1",
44
"main": "dist/node/index.js",
5+
"module": "dist/node-esm/index.mjs",
6+
"type": "commonjs",
7+
"exports": {
8+
".": {
9+
"import": "./dist/node-esm/index.mjs",
10+
"require": "./dist/node/index.js"
11+
},
12+
"./*": "./*",
13+
"./client": "./client.ts",
14+
"./clientTypes": "./clientTypes.d.ts"
15+
},
516
"bin": {
617
"vite-pages": "bin/vite-pages.js"
718
},
@@ -24,6 +35,7 @@
2435
"scripts": {
2536
"build": "rimraf ./dist && concurrently \"npm:build-*\"",
2637
"build-node": "tsc -p src/node",
38+
"build-node-esm": "rollup -c",
2739
"build-client": "tsc -p src/client",
2840
"dev": "rimraf ./dist && concurrently \"npm:dev-*\"",
2941
"dev-node": "tsc -w -p src/node",
@@ -34,6 +46,11 @@
3446
"vite": "^2.5.6 || ^3.0.0"
3547
},
3648
"devDependencies": {
49+
"@rollup/plugin-babel": "^5.3.0",
50+
"@rollup/plugin-commonjs": "^22.0.2",
51+
"@rollup/plugin-node-resolve": "^13.0.4",
52+
"@rollup/plugin-typescript": "^8.3.4",
53+
"@babel/preset-env": "^7.18.10",
3754
"@types/enhanced-resolve": "^3.0.6",
3855
"@types/fs-extra": "^9.0.8",
3956
"@types/minimist": "^1.2.0",

packages/react-pages/rollup.config.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import babel from '@rollup/plugin-babel'
2+
import commonjs from '@rollup/plugin-commonjs'
3+
import resolve from '@rollup/plugin-node-resolve'
4+
5+
const extensions = ['.js', '.jsx', '.ts', '.tsx']
6+
7+
/*
8+
use rollup to build esm code that target nodejs (run in native esm mode)
9+
instead of using tsc only
10+
to avoid error like this:
11+
12+
[ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/vite-plugin-react-pages/packages/react-pages/dist/node-esm/page-strategy/DefaultPageStrategy' is not supported resolving ES modules imported from /path/to/vite-plugin-react-pages/packages/react-pages/dist/node-esm/index.js
13+
14+
It is more flexable to use a bundler to build code for nodejs platform because:
15+
- it allows us to use directory import in source code
16+
- we don't need to add `.js` extension in relative import
17+
- we can bundle include some deps (currently we external all deps)
18+
*/
19+
20+
export default {
21+
input: 'src/node/index.ts',
22+
output: [
23+
{
24+
// dir: 'dist/node-esm',
25+
file: 'dist/node-esm/index.mjs',
26+
format: 'esm',
27+
sourcemap: true,
28+
},
29+
// {
30+
// dir: 'dist-cjs',
31+
// format: 'cjs',
32+
// sourcemap: true,
33+
// },
34+
],
35+
external: [],
36+
plugins: [
37+
resolve({
38+
// prevent bundling unexpected deps
39+
resolveOnly: ['none!'],
40+
extensions,
41+
}),
42+
commonjs(),
43+
babel({
44+
babelHelpers: 'bundled',
45+
extensions,
46+
presets: [
47+
// [
48+
// '@babel/preset-env',
49+
// {
50+
// targets: {
51+
// chrome: '90',
52+
// node: '12',
53+
// },
54+
// },
55+
// ],
56+
'@babel/preset-typescript',
57+
],
58+
plugins: [],
59+
configFile: false,
60+
}),
61+
],
62+
}

packages/react-pages/src/node/index.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default function pluginFactory(opts: PluginConfig = {}): Plugin {
7979
},
8080
},
8181
}),
82-
configResolved({ root, plugins, logger, command }) {
82+
async configResolved({ root, plugins, logger, command }) {
8383
isBuild = command === 'build'
8484
pagesDir = opts.pagesDir ?? path.resolve(root, 'pages')
8585
if (opts.pageStrategy) {
@@ -94,7 +94,9 @@ export default function pluginFactory(opts: PluginConfig = {}): Plugin {
9494

9595
if (mdxPlugin?.mdxOptions) {
9696
// Inject demo transformer
97-
mdxPlugin.mdxOptions.remarkPlugins.push(...getRemarkPlugins(root))
97+
mdxPlugin.mdxOptions.remarkPlugins.push(
98+
...(await getRemarkPlugins(root))
99+
)
98100
} else {
99101
logger.warn(
100102
'[react-pages] Please install [email protected] or higher'
@@ -212,7 +214,7 @@ export { extractStaticData, File } from './utils/virtual-module'
212214
export { PageStrategy }
213215
export { DefaultPageStrategy, defaultFileHandler }
214216

215-
function getRemarkPlugins(root: string) {
217+
async function getRemarkPlugins(root: string) {
216218
const result: any[] = [
217219
DemoMdxPlugin,
218220
TsInfoMdxPlugin,
@@ -226,10 +228,10 @@ function getRemarkPlugins(root: string) {
226228
// checkout playground/custom-find-pages2.
227229
const hasPkgJson = fs.pathExistsSync(pkgJsonPath)
228230

231+
const pkgJson = await fs.readJSON(pkgJsonPath)
232+
229233
// Inject frontmatter parser if missing
230-
const { devDependencies = {}, dependencies = {} } = hasPkgJson
231-
? require(pkgJsonPath)
232-
: {}
234+
const { devDependencies = {}, dependencies = {} } = hasPkgJson ? pkgJson : {}
233235
// By default we add remark-frontmatter automatically.
234236
// But if user install their own remark-frontmatter,
235237
// they are responsible to add the plugin manually
@@ -238,7 +240,9 @@ function getRemarkPlugins(root: string) {
238240
!devDependencies['remark-frontmatter'] &&
239241
!dependencies['remark-frontmatter']
240242
) {
241-
result.push(require('remark-frontmatter'))
243+
// result.push(require('remark-frontmatter'))
244+
const remarkFrontmatter = await import('remark-frontmatter')
245+
result.push(remarkFrontmatter.default || remarkFrontmatter)
242246
}
243247
return result
244248
}

0 commit comments

Comments
 (0)