packaton 0.0.17 → 0.0.22
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.d.ts +2 -1
- package/package.json +1 -1
- package/src/app-dev.js +6 -3
- package/src/config.js +1 -0
- package/src/plugins-dev/WatcherDevClient.js +3 -1
- package/src/plugins-dev/cache-bust-resolver.js +14 -0
- package/src/plugins-dev/watcherDev.js +6 -2
- package/src/plugins-prod/HtmlCompiler.js +18 -19
- package/src/plugins-prod/minifyJS.js +2 -2
package/index.d.ts
CHANGED
|
@@ -10,11 +10,12 @@ export interface Config {
|
|
|
10
10
|
port?: number
|
|
11
11
|
onReady?: (address: string) => void
|
|
12
12
|
hotReload?: boolean // For UI dev purposes only
|
|
13
|
+
watchIgnore?: Array<string|RegExp>
|
|
13
14
|
|
|
14
15
|
// Production
|
|
15
16
|
outputDir?: string
|
|
16
17
|
outputExtension?: string
|
|
17
|
-
minifyJS?: (js: string) => Promise<string>
|
|
18
|
+
minifyJS?: (js: string, isModule: boolean) => Promise<string>
|
|
18
19
|
minifyCSS?: (css: string) => Promise<string>
|
|
19
20
|
minifyHTML?: (html: string) => Promise<string>
|
|
20
21
|
sitemapDomain?: string
|
package/package.json
CHANGED
package/src/app-dev.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { register } from 'node:module'
|
|
2
|
+
import { createServer } from 'node:http'
|
|
2
3
|
|
|
3
4
|
import { router } from './router.js'
|
|
4
5
|
import { watchDev } from './plugins-dev/WatcherDevClient.js'
|
|
@@ -10,10 +11,12 @@ import { watchDev } from './plugins-dev/WatcherDevClient.js'
|
|
|
10
11
|
*/
|
|
11
12
|
export function devStaticPages(config) {
|
|
12
13
|
return new Promise((resolve, reject) => {
|
|
14
|
+
register('./plugins-dev/cache-bust-resolver.js', import.meta.url)
|
|
15
|
+
|
|
13
16
|
if (config.hotReload)
|
|
14
|
-
watchDev(config.srcPath)
|
|
17
|
+
watchDev(config.srcPath, config.watchIgnore)
|
|
15
18
|
|
|
16
|
-
const server =
|
|
19
|
+
const server = createServer(router(config))
|
|
17
20
|
server.on('error', reject)
|
|
18
21
|
server.listen(config.port, config.host, () => {
|
|
19
22
|
const addr = `http://${server.address().address}:${server.address().port}`
|
package/src/config.js
CHANGED
|
@@ -25,6 +25,7 @@ const schema = {
|
|
|
25
25
|
port: [0, port => Number.isInteger(port) && port >= 0 && port < 2 ** 16], // 0 means auto-assigned
|
|
26
26
|
onReady: [await openInBrowser, is(Function)],
|
|
27
27
|
hotReload: [true, is(Boolean)],
|
|
28
|
+
watchIgnore: [[], Array.isArray], // TODO Array<string|RegExp>
|
|
28
29
|
|
|
29
30
|
// Production
|
|
30
31
|
outputExtension: ['.html', optional(String)],
|
|
@@ -10,8 +10,10 @@ export const devClientWatcher = new class extends EventEmitter {
|
|
|
10
10
|
unsubscribe(listener) { this.removeListener('RELOAD', listener) }
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function watchDev(rootPath) {
|
|
13
|
+
export function watchDev(rootPath, watchIgnore) {
|
|
14
14
|
watch(rootPath, { recursive: true }, (_, file) => {
|
|
15
|
+
if (watchIgnore.some(f => f === file)) // TODO handle regexes
|
|
16
|
+
return
|
|
15
17
|
docs.onWatch(join(rootPath, file))
|
|
16
18
|
devClientWatcher.emit(file)
|
|
17
19
|
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// We register this hook at runtime so it doesn’t interfere with non-dynamic imports.
|
|
2
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
3
|
+
const result = await nextResolve(specifier, context);
|
|
4
|
+
if (result.url?.startsWith('file:')) {
|
|
5
|
+
const url = new URL(result.url)
|
|
6
|
+
url.searchParams.set('t', performance.now())
|
|
7
|
+
return {
|
|
8
|
+
...result,
|
|
9
|
+
url: url.href,
|
|
10
|
+
shortCircuit: true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return result
|
|
14
|
+
}
|
|
@@ -9,7 +9,7 @@ async function longPollDevChanges() {
|
|
|
9
9
|
|
|
10
10
|
const file = await response.json() || ''
|
|
11
11
|
if (file.endsWith('.css')) {
|
|
12
|
-
hotReloadCSS(file)
|
|
12
|
+
await hotReloadCSS(file)
|
|
13
13
|
longPollDevChanges()
|
|
14
14
|
}
|
|
15
15
|
else if (file)
|
|
@@ -23,11 +23,15 @@ async function longPollDevChanges() {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function hotReloadCSS(file) {
|
|
26
|
+
async function hotReloadCSS(file) {
|
|
27
27
|
let link = document.querySelector(`link[href^="/${file}"]`)
|
|
28
28
|
if (link) {
|
|
29
29
|
const [url] = link.getAttribute('href').split('?')
|
|
30
30
|
link.href = url + '?' + Date.now()
|
|
31
31
|
}
|
|
32
|
+
else {
|
|
33
|
+
const mod = await import(`/${file}?${Date.now()}`, { with: { type: 'css' } })
|
|
34
|
+
document.adoptedStyleSheets = [mod.default]
|
|
35
|
+
}
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -49,7 +49,7 @@ export class HtmlCompiler {
|
|
|
49
49
|
}
|
|
50
50
|
if (this.css) {
|
|
51
51
|
this.css = await this.#minifyCSS(this.css)
|
|
52
|
-
this
|
|
52
|
+
this.#appendToHead(`<style>${this.css}</style>`)
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -71,44 +71,36 @@ export class HtmlCompiler {
|
|
|
71
71
|
|
|
72
72
|
this.scriptsModuleJs = await Promise.all(scripts
|
|
73
73
|
.filter(([type]) => type === 'module')
|
|
74
|
-
.map(([, body]) => this.#minifyJS(body)))
|
|
74
|
+
.map(([, body]) => this.#minifyJS(body, Boolean('isModule'))))
|
|
75
75
|
|
|
76
|
-
this.scriptsNonJs = scripts.filter(([type]) => type !== 'application/javascript' && type !== 'module')
|
|
77
|
-
|
|
78
76
|
this.scriptsNonJs = scripts
|
|
79
77
|
.filter(([type]) => type !== 'application/javascript' && type !== 'module')
|
|
80
78
|
|
|
81
79
|
if (this.scriptsJs)
|
|
82
|
-
this
|
|
80
|
+
this.#appendToBody(`<script>${this.scriptsJs}</script>`)
|
|
83
81
|
|
|
84
82
|
for (const body of this.scriptsModuleJs)
|
|
85
|
-
this
|
|
83
|
+
this.#appendToBody(`<script type="module">${body}</script>`)
|
|
86
84
|
|
|
87
85
|
for (const [type, body] of this.scriptsNonJs)
|
|
88
|
-
this
|
|
89
|
-
|
|
86
|
+
this.#appendToBody(`<script type="${type}">${body}</script>`)
|
|
90
87
|
}
|
|
91
88
|
|
|
89
|
+
|
|
92
90
|
csp() {
|
|
93
|
-
const cssHash = this.css
|
|
94
|
-
? `'${this.hash256(this.css)}'`
|
|
95
|
-
: '' // TODO maybe self?
|
|
96
|
-
const jsScriptHash = this.scriptsJs
|
|
97
|
-
? `'${this.hash256(this.scriptsJs)}'`
|
|
98
|
-
: '' // TODO maybe self?
|
|
91
|
+
const cssHash = this.css ? `'${this.hash256(this.css)}'` : '' // TODO maybe self?
|
|
99
92
|
|
|
93
|
+
const jsScriptHash = this.scriptsJs ? `'${this.hash256(this.scriptsJs)}'` : '' // TODO maybe self?
|
|
100
94
|
const jsModulesHashes = this.scriptsModuleJs.map(body => `'${this.hash256(body)}'`).join(' ')
|
|
101
|
-
|
|
102
95
|
const nonJsScriptHashes = this.scriptsNonJs.map(([, body]) => `'${this.hash256(body)}'`).join(' ')
|
|
103
|
-
|
|
104
|
-
const externalScriptHashes = this.externalScripts.map(url => `${new URL(url).origin}`).join(' ')
|
|
105
|
-
|
|
106
96
|
const inlineScriptsHashes = this.extractInlineScripts().map(body => `'${this.hash256(body)}'`).join(' ')
|
|
97
|
+
const externalScriptDomains = this.externalScripts.map(url => `${new URL(url).origin}`).join(' ')
|
|
98
|
+
|
|
107
99
|
return [
|
|
108
100
|
`default-src 'self'`,
|
|
109
101
|
`img-src 'self' data:`, // data: is for Safari's video player icons and for CSS bg images
|
|
110
102
|
`style-src ${cssHash}`,
|
|
111
|
-
`script-src-elem ${nonJsScriptHashes} ${jsScriptHash} ${jsModulesHashes} ${
|
|
103
|
+
`script-src-elem ${nonJsScriptHashes} ${jsScriptHash} ${jsModulesHashes} ${externalScriptDomains} ${inlineScriptsHashes} 'self'`,
|
|
112
104
|
`frame-ancestors 'none'`
|
|
113
105
|
].join('; ')
|
|
114
106
|
}
|
|
@@ -119,10 +111,17 @@ export class HtmlCompiler {
|
|
|
119
111
|
: ''
|
|
120
112
|
}
|
|
121
113
|
|
|
114
|
+
#appendToHead(tag) {
|
|
115
|
+
this.html = this.html.replace('</head>', `\n${tag}</head>`)
|
|
116
|
+
}
|
|
117
|
+
#appendToBody(tag) {
|
|
118
|
+
this.html = this.html.replace('</body>', `\n${tag}</body>`)
|
|
119
|
+
}
|
|
122
120
|
removeLineContaining(str) {
|
|
123
121
|
this.html = this.html.replace(new RegExp('^.*' + str + '.*\n', 'm'), '')
|
|
124
122
|
}
|
|
125
123
|
|
|
124
|
+
|
|
126
125
|
extractStyleSheetHrefs() {
|
|
127
126
|
const reExtractStyleSheets = /(?<=<link\s.*href=")[^"]+\.css/g
|
|
128
127
|
return Array.from(this.html.matchAll(reExtractStyleSheets), m => m[0])
|