packaton 0.0.9 → 0.0.12
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/package.json
CHANGED
package/src/app-prod.js
CHANGED
|
@@ -23,8 +23,7 @@ export async function buildStaticPages(config) {
|
|
|
23
23
|
|
|
24
24
|
const pSource = config.srcPath
|
|
25
25
|
const pDist = config.outputDir
|
|
26
|
-
const pDistAssets = join(
|
|
27
|
-
const pDistMedia = join(pDist, MEDIA_REL_URL)
|
|
26
|
+
const pDistAssets = join(pDist, config.assetsDir)
|
|
28
27
|
|
|
29
28
|
const server = createServer(router(config))
|
|
30
29
|
server.on('error', reject)
|
|
@@ -42,19 +41,21 @@ export async function buildStaticPages(config) {
|
|
|
42
41
|
}
|
|
43
42
|
})
|
|
44
43
|
|
|
45
|
-
const mediaHashes = await renameMediaWithHashes(pDistMedia) // only on top dir
|
|
46
44
|
const pages = await crawlRoutes(server.address(), docs.routes)
|
|
47
|
-
|
|
45
|
+
const mediaHashes = await renameMediaWithHashes(pDist, MEDIA_REL_URL)
|
|
46
|
+
|
|
48
47
|
const cspByRoute = []
|
|
49
48
|
for (const [route, rawHtml] of pages) {
|
|
50
49
|
const doc = new HtmlCompiler(rawHtml, join(pSource, dirname(route)), {
|
|
51
50
|
minifyJS: config.minifyJS,
|
|
52
51
|
minifyCSS: config.minifyCSS,
|
|
53
52
|
minifyHTML: config.minifyHTML,
|
|
54
|
-
mediaRelUrl: MEDIA_REL_URL
|
|
53
|
+
mediaRelUrl: MEDIA_REL_URL,
|
|
54
|
+
mediaHashes
|
|
55
|
+
|
|
55
56
|
})
|
|
56
57
|
await doc.minifyHTML()
|
|
57
|
-
doc.remapMedia(
|
|
58
|
+
doc.remapMedia()
|
|
58
59
|
// TODO remap media in css and js
|
|
59
60
|
await doc.inlineMinifiedCSS()
|
|
60
61
|
await doc.inlineMinifiedJS()
|
|
@@ -24,9 +24,21 @@ async function longPollDevChanges() {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function hotReloadCSS(file) {
|
|
27
|
-
|
|
27
|
+
let link = document.querySelector(`link[href^="${file}"]`)
|
|
28
|
+
|
|
29
|
+
// Fallback: construct relative path based on current pathname
|
|
30
|
+
if (!link) {
|
|
31
|
+
const parts = window.location.pathname.split('/').filter(Boolean)
|
|
32
|
+
if (parts.length > 1)
|
|
33
|
+
parts.pop()
|
|
34
|
+
const urlDir = parts.join('/') + '/'
|
|
35
|
+
const r = file.split(urlDir).pop()
|
|
36
|
+
link = document.querySelector(`link[href^="${r}"]`)
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
if (link) {
|
|
29
|
-
const [url] = link.href.split('?')
|
|
40
|
+
const [url] = link.getAttribute('href').split('?')
|
|
30
41
|
link.href = url + '?' + Date.now()
|
|
31
42
|
}
|
|
32
43
|
}
|
|
44
|
+
|
|
@@ -11,6 +11,7 @@ export class HtmlCompiler {
|
|
|
11
11
|
css = ''
|
|
12
12
|
scriptsJs = ''
|
|
13
13
|
mediaRelUrl = ''
|
|
14
|
+
mediaHashes = new Map()
|
|
14
15
|
scriptsNonJs = ''
|
|
15
16
|
externalScripts = []
|
|
16
17
|
externalCSS = []
|
|
@@ -18,13 +19,14 @@ export class HtmlCompiler {
|
|
|
18
19
|
#minifyCSS = a => a
|
|
19
20
|
#minifyHTML = a => a
|
|
20
21
|
|
|
21
|
-
constructor(html, pSource = '', { minifyJS, minifyCSS, minifyHTML, mediaRelUrl }) {
|
|
22
|
+
constructor(html, pSource = '', { minifyJS, minifyCSS, minifyHTML, mediaRelUrl, mediaHashes }) {
|
|
22
23
|
this.html = html
|
|
23
24
|
this.pSource = pSource
|
|
24
25
|
this.#minifyJS = minifyJS
|
|
25
26
|
this.#minifyCSS = minifyCSS
|
|
26
27
|
this.#minifyHTML = minifyHTML
|
|
27
28
|
this.mediaRelUrl = mediaRelUrl
|
|
29
|
+
this.mediaHashes = mediaHashes
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
// Removes comments and format multi-line tags (needed for `removeLineContaining`)
|
|
@@ -32,8 +34,8 @@ export class HtmlCompiler {
|
|
|
32
34
|
this.html = await this.#minifyHTML(this.html)
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
remapMedia(
|
|
36
|
-
this.html = remapMediaInHTML(mediaHashes, this.html, this.mediaRelUrl)
|
|
37
|
+
remapMedia() {
|
|
38
|
+
this.html = remapMediaInHTML(this.mediaHashes, this.html, this.mediaRelUrl)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
async inlineMinifiedCSS() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { join, parse } from 'node:path'
|
|
2
1
|
import { renameSync } from 'node:fs'
|
|
2
|
+
import { join, parse, relative } from 'node:path'
|
|
3
3
|
import { sha1, listFiles } from '../utils/fs-utils.js'
|
|
4
4
|
|
|
5
5
|
|
|
@@ -7,49 +7,33 @@ import { sha1, listFiles } from '../utils/fs-utils.js'
|
|
|
7
7
|
* Subdirectories are ignored
|
|
8
8
|
* foo.avif -> foo-<sha1>.avif
|
|
9
9
|
*/
|
|
10
|
-
export async function renameMediaWithHashes(
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const newFile = join(dir,
|
|
17
|
-
mediaHashes.set(base, newFileName)
|
|
10
|
+
export async function renameMediaWithHashes(distDir, mediaDir) {
|
|
11
|
+
const mDir = join(distDir, mediaDir)
|
|
12
|
+
const map = new Map()
|
|
13
|
+
|
|
14
|
+
for (const file of await listFiles(mDir)) {
|
|
15
|
+
const { dir, name, ext } = parse(file)
|
|
16
|
+
const newFile = join(dir, name + '-' + sha1(file) + ext)
|
|
18
17
|
renameSync(file, newFile)
|
|
18
|
+
map.set(relative(distDir, file), relative(distDir, newFile))
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
return
|
|
21
|
+
return map
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Having one dir is kinda nice for nginx headers, but that's not an excuse nor solves nested dirs with same filename
|
|
25
|
-
|
|
26
|
-
// TODO for (b of base) find and replace base with new hash
|
|
27
|
-
// so it works in dirs outside media/
|
|
28
|
-
// mm currently a limitation is that the dictionary doesn't have the path, just the name, so
|
|
29
|
-
// filenames need to be unique, regardless of being in a subfolder
|
|
30
|
-
/**
|
|
31
|
-
* Edit the media source links in the HTML, so they have the new SHA-1 hashed
|
|
32
|
-
* filenames. Assumes that all the files are in "media/" (not ../media, ./media)
|
|
33
|
-
*
|
|
34
|
-
* If you want to handle CSS files, edit the regex so
|
|
35
|
-
* instead of checking `="` (e.g. src="img.png") also checks for `url(`
|
|
36
|
-
**/
|
|
37
24
|
export function remapMediaInHTML(mediaHashes, html, mediaRelUrl) {
|
|
38
|
-
const mURL =
|
|
39
|
-
const reFindMedia = new RegExp(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (const [, url] of html.matchAll(reFindMedia)) {
|
|
43
|
-
const hashedName = mediaHashes.get(url.replace(reFindMediaKey, ''))
|
|
25
|
+
const mURL = escapeRegex(mediaRelUrl)
|
|
26
|
+
const reFindMedia = new RegExp(`="(/?)(${mURL}/[^"]*)"`, 'g')
|
|
27
|
+
return html.replace(reFindMedia, (_, optLeadingSlash, url) => {
|
|
28
|
+
const hashedName = mediaHashes.get(url)
|
|
44
29
|
if (!hashedName)
|
|
45
|
-
throw `ERROR: Missing ${url}
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
return html
|
|
30
|
+
throw new Error(`ERROR: Missing ${url}`)
|
|
31
|
+
return `="${optLeadingSlash}${hashedName}"`
|
|
32
|
+
})
|
|
49
33
|
}
|
|
50
34
|
|
|
51
35
|
|
|
52
|
-
function
|
|
36
|
+
function escapeRegex(literal) {
|
|
53
37
|
return literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
54
38
|
}
|
|
55
39
|
|
package/src/router.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises'
|
|
|
3
3
|
|
|
4
4
|
import { docs } from './app.js'
|
|
5
5
|
import { mimeFor } from './utils/mimes.js'
|
|
6
|
+
import { isDirectory } from './utils/fs-utils.js'
|
|
6
7
|
import { devClientWatcher } from './plugins-dev/WatcherDevClient.js'
|
|
7
8
|
import { sendError, sendJSON, servePartialContent, serveAsset } from './utils/http-response.js'
|
|
8
9
|
|
|
@@ -14,7 +15,6 @@ const API = {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
|
|
18
18
|
/** @param {Config} config */
|
|
19
19
|
export function router({ srcPath, ignore, mode }) {
|
|
20
20
|
docs.init(srcPath, ignore)
|
|
@@ -24,21 +24,21 @@ export function router({ srcPath, ignore, mode }) {
|
|
|
24
24
|
try {
|
|
25
25
|
if (url === API.watchDev)
|
|
26
26
|
longPollDevHotReload(req, response)
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
else if (url === WATCHER_DEV)
|
|
29
29
|
serveAsset(response, join(import.meta.dirname, url))
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
else if (docs.hasRoute(url))
|
|
32
32
|
await serveDocument(response, docs.fileFor(url), isDev)
|
|
33
|
-
|
|
34
|
-
else if (docs.hasRoute(join(url, 'index')))
|
|
33
|
+
|
|
34
|
+
else if (docs.hasRoute(join(url, 'index')))
|
|
35
35
|
await serveDocument(response, docs.fileFor(join(url, 'index')), isDev)
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
else if (req.headers.range)
|
|
38
|
-
await servePartialContent(response, req.headers,
|
|
39
|
-
|
|
38
|
+
await servePartialContent(response, req.headers, resolveUrl(req, srcPath, url))
|
|
39
|
+
|
|
40
40
|
else
|
|
41
|
-
serveAsset(response,
|
|
41
|
+
serveAsset(response, resolveUrl(req, srcPath, url))
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
44
|
sendError(response, error)
|
|
@@ -57,6 +57,20 @@ async function serveDocument(response, file, isDev) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
function resolveUrl(req, srcPath, url) {
|
|
61
|
+
return join(srcPath, dirFor(req.headers.referer, srcPath), url)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function dirFor(referer = '/', srcPath) {
|
|
65
|
+
if (referer.endsWith('/'))
|
|
66
|
+
return ''
|
|
67
|
+
const p = new URL(referer).pathname
|
|
68
|
+
return isDirectory(join(srcPath, p))
|
|
69
|
+
? p
|
|
70
|
+
: ''
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
60
74
|
const LONG_POLL_SERVER_TIMEOUT = 8000
|
|
61
75
|
|
|
62
76
|
function longPollDevHotReload(req, response) {
|
package/src/utils/fs-utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdir } from 'node:fs/promises'
|
|
2
2
|
import { createHash } from 'node:crypto'
|
|
3
3
|
import { join, dirname } from 'node:path'
|
|
4
|
-
import { rmSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { rmSync, lstatSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs'
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export const read = f => readFileSync(f, 'utf8')
|
|
@@ -29,7 +29,7 @@ export const sha1 = f => createHash('sha1').update(readFileSync(f)).digest('base
|
|
|
29
29
|
export async function listFiles(dir) {
|
|
30
30
|
return (await readdir(dir, {
|
|
31
31
|
withFileTypes: true,
|
|
32
|
-
recursive:
|
|
32
|
+
recursive: true
|
|
33
33
|
}))
|
|
34
34
|
.filter(e => e.isFile())
|
|
35
35
|
.map(e => join(e.parentPath, e.name))
|