@jsenv/plugin-preact 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -0
- package/package.json +27 -0
- package/src/client/prefresh.js +36 -0
- package/src/jsenv_plugin_preact.js +174 -0
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { jsenvPluginPreact } from "./src/jsenv_plugin_preact.js"
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jsenv/plugin-preact",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/jsenv/jsenv-core",
|
|
8
|
+
"directory": "packages/node-esm-resolution"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public",
|
|
12
|
+
"registry": "https://registry.npmjs.org"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@babel/plugin-transform-react-jsx": "7.17.3",
|
|
17
|
+
"@jsenv/utils": "1.0.0",
|
|
18
|
+
"@jsenv/url-meta": "6.2.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@babel/plugin-transform-react-jsx-development": "7.16.7",
|
|
22
|
+
"@prefresh/babel-plugin": "0.4.3",
|
|
23
|
+
"@prefresh/core": "1.3.4",
|
|
24
|
+
"@prefresh/utils": "1.1.3",
|
|
25
|
+
"babel-plugin-transform-hook-names": "1.0.2"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* globals self */
|
|
2
|
+
|
|
3
|
+
import "@prefresh/core"
|
|
4
|
+
import { flush } from "@prefresh/utils"
|
|
5
|
+
|
|
6
|
+
export const installPrefresh = (url) => {
|
|
7
|
+
let prevRefreshReg = self.$RefreshReg$ || (() => {})
|
|
8
|
+
let prevRefreshSig = self.$RefreshSig$ || (() => (type) => type)
|
|
9
|
+
self.$RefreshReg$ = (type, id) => {
|
|
10
|
+
self.__PREFRESH__.register(type, `${url} ${id}`)
|
|
11
|
+
}
|
|
12
|
+
self.$RefreshSig$ = () => {
|
|
13
|
+
let status = "begin"
|
|
14
|
+
let savedType
|
|
15
|
+
return (type, key, forceReset, getCustomHooks) => {
|
|
16
|
+
if (!savedType) savedType = type
|
|
17
|
+
status = self.__PREFRESH__.sign(
|
|
18
|
+
type || savedType,
|
|
19
|
+
key,
|
|
20
|
+
forceReset,
|
|
21
|
+
getCustomHooks,
|
|
22
|
+
status,
|
|
23
|
+
)
|
|
24
|
+
return type
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
end: () => {
|
|
29
|
+
self.$RefreshReg$ = prevRefreshReg
|
|
30
|
+
self.$RefreshSig$ = prevRefreshSig
|
|
31
|
+
},
|
|
32
|
+
acceptCallback: () => {
|
|
33
|
+
flush()
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* - https://github.com/preactjs/preset-vite/blob/main/src/index.ts
|
|
3
|
+
* - https://github.com/preactjs/prefresh/blob/main/packages/vite/src/index.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { normalizeStructuredMetaMap, urlToMeta } from "@jsenv/url-meta"
|
|
7
|
+
|
|
8
|
+
import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js"
|
|
9
|
+
import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
|
|
10
|
+
import { composeTwoSourcemaps } from "@jsenv/utils/sourcemap/sourcemap_composition_v3.js"
|
|
11
|
+
import {
|
|
12
|
+
parseHtmlString,
|
|
13
|
+
stringifyHtmlAst,
|
|
14
|
+
injectScriptAsEarlyAsPossible,
|
|
15
|
+
createHtmlNode,
|
|
16
|
+
} from "@jsenv/utils/html_ast/html_ast.js"
|
|
17
|
+
|
|
18
|
+
export const jsenvPluginPreact = ({
|
|
19
|
+
hotRefreshPatterns = {
|
|
20
|
+
"./**/*.jsx": true,
|
|
21
|
+
"./**/*.tsx": true,
|
|
22
|
+
"./**/node_modules/": false,
|
|
23
|
+
},
|
|
24
|
+
preactDevtoolsDuringBuild = false,
|
|
25
|
+
} = {}) => {
|
|
26
|
+
const structuredMetaMap = normalizeStructuredMetaMap(
|
|
27
|
+
{
|
|
28
|
+
hot: hotRefreshPatterns,
|
|
29
|
+
},
|
|
30
|
+
"file://",
|
|
31
|
+
)
|
|
32
|
+
const shouldEnablePrefresh = (url) => {
|
|
33
|
+
return urlToMeta({ url, structuredMetaMap }).hot
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
name: "jsenv:preact",
|
|
38
|
+
appliesDuring: "*",
|
|
39
|
+
resolve: {
|
|
40
|
+
js_import_export: (reference, { resolveReference }) => {
|
|
41
|
+
if (
|
|
42
|
+
reference.specifier === "react" ||
|
|
43
|
+
reference.specifier === "react-dom"
|
|
44
|
+
) {
|
|
45
|
+
reference.specifier = "preact/compat"
|
|
46
|
+
return resolveReference(reference).url
|
|
47
|
+
}
|
|
48
|
+
return null
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
transform: {
|
|
52
|
+
html: ({ content }, { scenario, referenceUtils }) => {
|
|
53
|
+
if (!preactDevtoolsDuringBuild && scenario === "build") {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
const htmlAst = parseHtmlString(content)
|
|
57
|
+
const [preactDevtoolsReference] = referenceUtils.inject({
|
|
58
|
+
type: "js_import_export",
|
|
59
|
+
specifier:
|
|
60
|
+
scenario === "dev" || scenario === "test"
|
|
61
|
+
? "preact/debug"
|
|
62
|
+
: "preact/devtools",
|
|
63
|
+
})
|
|
64
|
+
injectScriptAsEarlyAsPossible(
|
|
65
|
+
htmlAst,
|
|
66
|
+
createHtmlNode({
|
|
67
|
+
"tagName": "script",
|
|
68
|
+
"type": "module",
|
|
69
|
+
"textContent": `
|
|
70
|
+
import ${preactDevtoolsReference.generatedSpecifier}
|
|
71
|
+
`,
|
|
72
|
+
"injected-by": "jsenv:preact",
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
75
|
+
const htmlModified = stringifyHtmlAst(htmlAst)
|
|
76
|
+
return { content: htmlModified }
|
|
77
|
+
},
|
|
78
|
+
js_module: async (
|
|
79
|
+
{ url, generatedUrl, content },
|
|
80
|
+
{ scenario, referenceUtils },
|
|
81
|
+
) => {
|
|
82
|
+
// case "Fragment":
|
|
83
|
+
// return `${source}/${development ? "jsx-dev-runtime" : "jsx-runtime"}`;
|
|
84
|
+
// case "jsxDEV":
|
|
85
|
+
// return `${source}/jsx-dev-runtime`;
|
|
86
|
+
// case "jsx":
|
|
87
|
+
// case "jsxs":
|
|
88
|
+
// return `${source}/jsx-runtime`;
|
|
89
|
+
// case "createElement":
|
|
90
|
+
// return source;
|
|
91
|
+
const prefreshEnabled =
|
|
92
|
+
scenario === "dev" ? shouldEnablePrefresh(url) : false
|
|
93
|
+
const hookNamesEnabled = scenario === "dev"
|
|
94
|
+
const { code, map } = await applyBabelPlugins({
|
|
95
|
+
babelPlugins: [
|
|
96
|
+
[
|
|
97
|
+
scenario === "dev"
|
|
98
|
+
? "@babel/plugin-transform-react-jsx-development"
|
|
99
|
+
: "@babel/plugin-transform-react-jsx",
|
|
100
|
+
{
|
|
101
|
+
runtime: "automatic",
|
|
102
|
+
importSource: "preact",
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
...(hookNamesEnabled ? ["babel-plugin-transform-hook-names"] : []),
|
|
106
|
+
...(prefreshEnabled ? ["@prefresh/babel-plugin"] : []),
|
|
107
|
+
],
|
|
108
|
+
url,
|
|
109
|
+
generatedUrl,
|
|
110
|
+
content,
|
|
111
|
+
})
|
|
112
|
+
const magicSource = createMagicSource(code)
|
|
113
|
+
// When a plugin wants to inject import, it must use "generatedSpecifier" returned by "addReference":
|
|
114
|
+
// const preactJsxRuntimeReference = addReference({
|
|
115
|
+
// type: "js_import_export",
|
|
116
|
+
// specifier: "preact/jsx-runtime"
|
|
117
|
+
// })
|
|
118
|
+
// console.log(preactJsxRuntimeReference.generatedSpecifier)
|
|
119
|
+
// -> "/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js"
|
|
120
|
+
// Here "@babel/plugin-transform-react-jsx" is injecting some of these 3 imports into the code:
|
|
121
|
+
// 1. import { jsx } from "preact/jsx-runtime"
|
|
122
|
+
// 2. import { jsxDev } from "preact/jsx-dev-runtime"
|
|
123
|
+
// 3. import { createElement } from "preact"
|
|
124
|
+
// see https://github.com/babel/babel/blob/410c9acf1b9212cac69d50b5bb2015b9f372acc4/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L743-L755
|
|
125
|
+
// "@babel/plugin-transform-react-jsx" cannot be configured to inject
|
|
126
|
+
// "/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js" instead of "preact/jsx-runtime"
|
|
127
|
+
// But that's fine we can still replace these imports afterwards as done below
|
|
128
|
+
const injectedSpecifiers = [
|
|
129
|
+
`"preact"`,
|
|
130
|
+
`"preact/jsx-dev-runtime"`,
|
|
131
|
+
`"preact/jsx-runtime"`,
|
|
132
|
+
]
|
|
133
|
+
for (const importSpecifier of injectedSpecifiers) {
|
|
134
|
+
const index = code.indexOf(importSpecifier)
|
|
135
|
+
if (index > -1) {
|
|
136
|
+
magicSource.replace({
|
|
137
|
+
start: index,
|
|
138
|
+
end: index + importSpecifier.length,
|
|
139
|
+
replacement: referenceUtils.inject({
|
|
140
|
+
type: "js_import_export",
|
|
141
|
+
specifier: importSpecifier.slice(1, -1),
|
|
142
|
+
}).generatedSpecifier,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (prefreshEnabled) {
|
|
147
|
+
const hasReg = /\$RefreshReg\$\(/.test(code)
|
|
148
|
+
const hasSig = /\$RefreshSig\$\(/.test(code)
|
|
149
|
+
if (hasReg || hasSig) {
|
|
150
|
+
const prefreshClientFileReference = referenceUtils.inject({
|
|
151
|
+
type: "js_import_export",
|
|
152
|
+
specifier: "@jsenv/plugin-preact/src/client/prefresh.js",
|
|
153
|
+
})
|
|
154
|
+
magicSource.prepend(`import { installPrefresh } from ${
|
|
155
|
+
prefreshClientFileReference.generatedSpecifier
|
|
156
|
+
}
|
|
157
|
+
const __prefresh__ = installPrefresh(${JSON.stringify(url)})
|
|
158
|
+
`)
|
|
159
|
+
if (hasReg) {
|
|
160
|
+
magicSource.append(`
|
|
161
|
+
__prefresh__.end()
|
|
162
|
+
import.meta.hot.accept(__prefresh__.acceptCallback)`)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const result = magicSource.toContentAndSourcemap()
|
|
167
|
+
return {
|
|
168
|
+
content: result.content,
|
|
169
|
+
sourcemap: await composeTwoSourcemaps(map, result.sourcemap),
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
}
|