minista 4.0.0 → 4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minista",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "minista": "./bin/minista.js"
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@mdx-js/rollup": "^3.1.1",
69
- "@swc/core": "^1.15.18",
69
+ "@swc/core": "^1.15.21",
70
70
  "archiver": "^7.0.1",
71
71
  "cross-spawn": "^7.0.6",
72
72
  "image-size": "^2.0.2",
@@ -75,7 +75,7 @@
75
75
  "mojigiri": "^0.3.0",
76
76
  "node-html-parser": "^7.1.0",
77
77
  "picocolors": "^1.1.1",
78
- "picomatch": "^4.0.3",
78
+ "picomatch": "^4.0.4",
79
79
  "remark-frontmatter": "^5.0.0",
80
80
  "remark-mdx-frontmatter": "^5.2.0",
81
81
  "sharp": "^0.34.5",
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
 
@@ -42,7 +42,7 @@ export function pluginArchive(uOpts = {}) {
42
42
  enforce: "post",
43
43
  apply(_, { command, isSsrBuild }) {
44
44
  isDev = command === "serve"
45
- isSsr = command === "build" && isSsrBuild
45
+ isSsr = command === "build" && Boolean(isSsrBuild)
46
46
  isBuild = command === "build" && !isSsrBuild
47
47
  return isBuild
48
48
  },
@@ -79,7 +79,7 @@ export function pluginArchive(uOpts = {}) {
79
79
  reject(err)
80
80
  }
81
81
  })
82
- output.on("close", () => resolve())
82
+ output.on("close", () => resolve(undefined))
83
83
 
84
84
  archive.pipe(output)
85
85
  archive.glob(`${normalizePath(srcDir)}/**/*`, {
@@ -96,15 +96,19 @@ export function pluginArchive(uOpts = {}) {
96
96
  console.log(
97
97
  pc.gray(
98
98
  normalizePath(rel + path.sep) +
99
- pc.green(path.basename(finalPath))
100
- )
99
+ pc.green(path.basename(finalPath)),
100
+ ),
101
101
  )
102
102
  } catch (err) {
103
- console.error(
104
- pc.red(`Error creating archive ${outName}: ${err.message}`)
105
- )
103
+ if (err instanceof Error) {
104
+ console.error(
105
+ pc.red(`Error creating archive ${outName}: ${err.message}`),
106
+ )
107
+ } else {
108
+ console.error(pc.red(`An unknown error occurred: ${err}`))
109
+ }
106
110
  }
107
- })
111
+ }),
108
112
  )
109
113
  },
110
114
  }
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
 
@@ -46,7 +46,7 @@ export function pluginBeautify(uOpts = {}) {
46
46
  enforce: "post",
47
47
  apply(_, { command, isSsrBuild }) {
48
48
  isDev = command === "serve"
49
- isSsr = command === "build" && isSsrBuild
49
+ isSsr = command === "build" && Boolean(isSsrBuild)
50
50
  isBuild = command === "build" && !isSsrBuild
51
51
  return isBuild
52
52
  },
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
 
@@ -56,7 +56,7 @@ export function pluginBundle(uOpts = {}) {
56
56
  enforce: "pre",
57
57
  apply(_, { command, isSsrBuild }) {
58
58
  isDev = command === "serve"
59
- isSsr = command === "build" && isSsrBuild
59
+ isSsr = command === "build" && Boolean(isSsrBuild)
60
60
  isBuild = command === "build" && !isSsrBuild
61
61
  return isDev || isBuild
62
62
  },
@@ -134,12 +134,20 @@ export function pluginBundle(uOpts = {}) {
134
134
  cssFiles = []
135
135
  }
136
136
 
137
- imageFiles = [...importedImageFiles].map((file) => {
138
- const targetItem = Object.values(outputAssets).find((item) =>
139
- item.originalFileNames.some((name) => name === file)
137
+ imageFiles = [...importedImageFiles]
138
+ .map((file) => {
139
+ const targetItem = Object.values(outputAssets).find((item) =>
140
+ item.originalFileNames.some((name) => name === file),
141
+ )
142
+ return targetItem?.fileName
143
+ })
144
+ .filter(
145
+ /**
146
+ * @param {string | undefined} file
147
+ * @returns {file is string}
148
+ */
149
+ (file) => Boolean(file),
140
150
  )
141
- return targetItem.fileName
142
- })
143
151
 
144
152
  const htmlItems = Object.values(outputAssets).filter((item) => {
145
153
  return item.fileName.endsWith(".html")
@@ -160,7 +168,7 @@ export function pluginBundle(uOpts = {}) {
160
168
  const basedAssetUrl = getBasedAssetUrl(base, htmlName, file)
161
169
  const regExp = new RegExp(
162
170
  `(<[^>]*?)(?<!\\.)/${file}([^>]*?>)`,
163
- "gs"
171
+ "gs",
164
172
  )
165
173
  newHtml = newHtml.replace(regExp, `$1${basedAssetUrl}$2`)
166
174
  }
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types.js').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types.js').UserPluginOptions} UserPluginOptions */
4
4
 
@@ -27,7 +27,7 @@ export function pluginComment(uOpts = {}) {
27
27
  enforce: "pre",
28
28
  apply(_, { command, isSsrBuild }) {
29
29
  isDev = command === "serve"
30
- isSsr = command === "build" && isSsrBuild
30
+ isSsr = command === "build" && Boolean(isSsrBuild)
31
31
  isBuild = command === "build" && !isSsrBuild
32
32
  return isDev || isBuild
33
33
  },
@@ -47,7 +47,7 @@ export function pluginComment(uOpts = {}) {
47
47
  async generateBundle(options, bundle) {
48
48
  const outputAssets = filterOutputAssets(bundle)
49
49
  const htmlItems = Object.values(outputAssets).filter((item) =>
50
- item.fileName.endsWith(".html")
50
+ item.fileName.endsWith(".html"),
51
51
  )
52
52
 
53
53
  for (const item of htmlItems) {
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types.js').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types.js').UserPluginOptions} UserPluginOptions */
4
4
  /** @typedef {import('../ssg/types.js').SsgPage} SsgPage */
@@ -52,7 +52,7 @@ export function pluginEntry(uOpts = {}) {
52
52
  enforce: "pre",
53
53
  apply(_, { command, isSsrBuild }) {
54
54
  isDev = command === "serve"
55
- isSsr = command === "build" && isSsrBuild
55
+ isSsr = command === "build" && Boolean(isSsrBuild)
56
56
  isBuild = command === "build" && !isSsrBuild
57
57
  return isBuild
58
58
  },
@@ -72,7 +72,7 @@ export function pluginEntry(uOpts = {}) {
72
72
  const ssgFileUrl = pathToFileURL(path.resolve(ssgDir, file)).href
73
73
  const { ssgPages } = await import(ssgFileUrl)
74
74
  return ssgPages
75
- })
75
+ }),
76
76
  )
77
77
  ).flat()
78
78
 
@@ -111,7 +111,7 @@ export function pluginEntry(uOpts = {}) {
111
111
  } catch {
112
112
  return null
113
113
  }
114
- })
114
+ }),
115
115
  )
116
116
  for (const pair of checks) {
117
117
  if (pair) entries[pair[0]] = pair[1]
@@ -138,9 +138,10 @@ export function pluginEntry(uOpts = {}) {
138
138
  for (const item of Object.values(outputChunks)) {
139
139
  if (item.name !== entryId) continue
140
140
  if (!item.code.trim()) continue
141
+ if (!item.facadeModuleId) continue
141
142
 
142
143
  const before = normalizePath(
143
- path.relative(rootDir, item.facadeModuleId)
144
+ path.relative(rootDir, item.facadeModuleId),
144
145
  )
145
146
  const newFileName = item.fileName
146
147
  entryChanges[before] = newFileName
@@ -174,7 +175,7 @@ export function pluginEntry(uOpts = {}) {
174
175
  const basedAssetUrl = getBasedAssetUrl(base, htmlName, after)
175
176
  const regExp = new RegExp(
176
177
  `(<[^>]*?)(?<!\\.)/${before}([^>]*?>)`,
177
- "gs"
178
+ "gs",
178
179
  )
179
180
  newHtml = newHtml.replace(regExp, `$1${basedAssetUrl}$2`)
180
181
 
@@ -91,7 +91,7 @@ export function Picture({
91
91
  }
92
92
  const sourceOptimizeObj = cleanObj(sourceOptimizeObjRaw)
93
93
  const sourceOptimizeStr = JSON.stringify(
94
- mergeObj(defaultOptimize, sourceOptimizeObj)
94
+ mergeObj(defaultOptimize, sourceOptimizeObj),
95
95
  )
96
96
 
97
97
  return createElement("source", {
@@ -123,7 +123,7 @@ export function Picture({
123
123
  }
124
124
  const sourceOptimizeObj = cleanObj(sourceOptimizeObjRaw)
125
125
  const sourceOptimizeStr = JSON.stringify(
126
- mergeObj(defaultOptimize, sourceOptimizeObj)
126
+ mergeObj(defaultOptimize, sourceOptimizeObj),
127
127
  )
128
128
 
129
129
  return createElement("source", {
@@ -156,7 +156,7 @@ export function Picture({
156
156
  }
157
157
  const sourceOptimizeObj = cleanObj(sourceOptimizeObjRaw)
158
158
  const sourceOptimizeStr = JSON.stringify(
159
- mergeObj(defaultOptimize, sourceOptimizeObj)
159
+ mergeObj(defaultOptimize, sourceOptimizeObj),
160
160
  )
161
161
 
162
162
  return createElement("source", {
@@ -183,6 +183,6 @@ export function Picture({
183
183
  "data-minista-image-src": src,
184
184
  "data-minista-image-optimize": optimizeStr,
185
185
  ...attributes,
186
- })
186
+ }),
187
187
  )
188
188
  }
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import ('node-html-parser').HTMLElement} HTMLElement */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
  /** @typedef {import('./types').PluginOptions} PluginOptions */
@@ -33,7 +33,11 @@ import {
33
33
  getBuildBase,
34
34
  getBasedAssetUrl,
35
35
  } from "../../shared/url.js"
36
- import { mergeAlias, filterOutputAssets } from "../../shared/vite.js"
36
+ import {
37
+ mergeSsrNoExternal,
38
+ mergeAlias,
39
+ filterOutputAssets,
40
+ } from "../../shared/vite.js"
37
41
 
38
42
  const __filename = fileURLToPath(import.meta.url)
39
43
  const __dirname = path.dirname(__filename)
@@ -73,10 +77,10 @@ export function pluginImage(uOpts = {}) {
73
77
  const srcAttr = "data-minista-image-src"
74
78
  const optimizeAttr = "data-minista-image-optimize"
75
79
  const cpImagePath = normalizePath(
76
- path.resolve(__dirname, "components/image.js")
80
+ path.resolve(__dirname, "components/image.js"),
77
81
  )
78
82
  const cpPicturePath = normalizePath(
79
- path.resolve(__dirname, "components/picture.js")
83
+ path.resolve(__dirname, "components/picture.js"),
80
84
  )
81
85
 
82
86
  let isDev = false
@@ -120,7 +124,7 @@ export function pluginImage(uOpts = {}) {
120
124
  async function selfLoadCache() {
121
125
  if (useCache && fs.existsSync(imageCacheFile)) {
122
126
  imageCache = JSON.parse(
123
- await fs.promises.readFile(imageCacheFile, "utf8")
127
+ await fs.promises.readFile(imageCacheFile, "utf8"),
124
128
  )
125
129
  }
126
130
  if (useCache) {
@@ -136,7 +140,7 @@ export function pluginImage(uOpts = {}) {
136
140
  await fs.promises.writeFile(
137
141
  imageCacheFile,
138
142
  JSON.stringify(imageCache, null, 2),
139
- "utf8"
143
+ "utf8",
140
144
  )
141
145
  }
142
146
 
@@ -184,7 +188,7 @@ export function pluginImage(uOpts = {}) {
184
188
  await fs.promises.writeFile(fullPath, data, "utf8")
185
189
 
186
190
  urlNameMap[remoteUrl] = normalizePath(path.relative(rootDir, fullPath))
187
- })
191
+ }),
188
192
  )
189
193
  }
190
194
 
@@ -220,7 +224,7 @@ export function pluginImage(uOpts = {}) {
220
224
  ...(recipeMap[bufferHash]?.usedPatternMap || {}),
221
225
  },
222
226
  }
223
- })
227
+ }),
224
228
  )
225
229
  }
226
230
 
@@ -231,9 +235,9 @@ export function pluginImage(uOpts = {}) {
231
235
  const tagName = el.tagName.toLowerCase()
232
236
  const isTarget = ["img", "source"].includes(tagName)
233
237
 
234
- let imageName = el.getAttribute(srcAttr).replace(/^\//, "")
238
+ let imageName = el.getAttribute(srcAttr)?.replace(/^\//, "")
235
239
 
236
- if (!isTarget || !imageName) return
240
+ if (!isTarget || !imageName) return {}
237
241
 
238
242
  if (imageName.startsWith("http")) {
239
243
  imageName = urlNameMap[imageName]
@@ -276,10 +280,10 @@ export function pluginImage(uOpts = {}) {
276
280
  if (isBuild) entries[pathId] = outFullPath
277
281
  recipe.usedPatternMap[patternHash] = pattern
278
282
  delete recipe.patternMap[patternHash]
279
- }
280
- )
283
+ },
284
+ ),
281
285
  )
282
- })
286
+ }),
283
287
  )
284
288
  }
285
289
 
@@ -288,7 +292,7 @@ export function pluginImage(uOpts = {}) {
288
292
  enforce: "pre",
289
293
  apply(_, { command, isSsrBuild }) {
290
294
  isDev = command === "serve"
291
- isSsr = command === "build" && isSsrBuild
295
+ isSsr = command === "build" && Boolean(isSsrBuild)
292
296
  isBuild = command === "build" && !isSsrBuild
293
297
  return isDev || isSsr || isBuild
294
298
  },
@@ -307,6 +311,9 @@ export function pluginImage(uOpts = {}) {
307
311
  if (isDev) {
308
312
  base = getServeBase(config.base || base)
309
313
  return {
314
+ ssr: {
315
+ noExternal: mergeSsrNoExternal(config, ["minista"]),
316
+ },
310
317
  resolve: {
311
318
  alias: mergeAlias(config, [
312
319
  {
@@ -317,6 +324,13 @@ export function pluginImage(uOpts = {}) {
317
324
  },
318
325
  }
319
326
  }
327
+ if (isSsr) {
328
+ return {
329
+ ssr: {
330
+ noExternal: mergeSsrNoExternal(config, ["minista"]),
331
+ },
332
+ }
333
+ }
320
334
  if (isBuild) {
321
335
  base = getBuildBase(config.base || base)
322
336
 
@@ -331,7 +345,7 @@ export function pluginImage(uOpts = {}) {
331
345
  const ssgFileUrl = pathToFileURL(path.resolve(ssgDir, file)).href
332
346
  const { ssgPages } = await import(ssgFileUrl)
333
347
  return ssgPages
334
- })
348
+ }),
335
349
  )
336
350
  ).flat()
337
351
 
@@ -353,6 +367,7 @@ export function pluginImage(uOpts = {}) {
353
367
 
354
368
  for (const el of targetEls) {
355
369
  const { optimize, recipe, view } = selfGetElData(el)
370
+ if (!optimize || !recipe || !view) continue
356
371
  const patternMap = getPatternMap(optimize, recipe, view, false)
357
372
 
358
373
  for (const [patternHash, pattern] of Object.entries(patternMap)) {
@@ -390,6 +405,7 @@ export function pluginImage(uOpts = {}) {
390
405
 
391
406
  for (const el of targetEls) {
392
407
  const { tagName, optimize, recipe, view } = selfGetElData(el)
408
+ if (!optimize || !recipe || !view) continue
393
409
  const patternMap = getPatternMap(optimize, recipe, view, true)
394
410
 
395
411
  for (const [patternHash, pattern] of Object.entries(patternMap)) {
@@ -463,6 +479,7 @@ export function pluginImage(uOpts = {}) {
463
479
 
464
480
  for (const el of targetEls) {
465
481
  const { tagName, optimize, recipe, view } = selfGetElData(el)
482
+ if (!optimize || !recipe || !view) continue
466
483
  const attrs = getPatternAttrs(optimize, recipe, view, false)
467
484
  const srcset = Object.entries(attrs.srcset)
468
485
  .map(([size, before]) => {
@@ -10,8 +10,8 @@ export type ImageFormat = "inherit" | "jpg" | "png" | "webp" | "avif"
10
10
  export type ResolvedImageFormat = Omit<ImageFormat, "inherit">
11
11
 
12
12
  export type ImageOptimize = {
13
- outName?: string
14
- remoteName?: string
13
+ outName: string
14
+ remoteName: string
15
15
  layout: "constrained" | "fixed"
16
16
  breakpoints: number[] | { count: number; minWidth: number; maxWidth: number }
17
17
  resolutions: number[]
@@ -23,7 +23,7 @@ export function getPatternName(
23
23
  remoteName,
24
24
  width,
25
25
  height,
26
- format
26
+ format,
27
27
  ) {
28
28
  const parsed = path.parse(fileName)
29
29
 
@@ -49,6 +49,7 @@ export function getPatternName(
49
49
  * @returns {{[patternHash: string]: ImagePattern}|{}}
50
50
  */
51
51
  export function getPatternMap(optimize, recipe, view, resizeOnly) {
52
+ /** @type {{[patternHash: string]: ImagePattern}} */
52
53
  let result = {}
53
54
 
54
55
  const {
@@ -95,7 +96,7 @@ export function getPatternMap(optimize, recipe, view, resizeOnly) {
95
96
  remoteName,
96
97
  width,
97
98
  height,
98
- format
99
+ format,
99
100
  )
100
101
 
101
102
  /** @type {ImagePattern} */
@@ -121,6 +122,7 @@ export function getPatternMap(optimize, recipe, view, resizeOnly) {
121
122
  * @returns {{ srcset: { [key: string]: string }, src: string }}
122
123
  */
123
124
  export function getPatternAttrs(optimize, recipe, view, resizeOnly) {
125
+ /** @type {{ srcset: { [key: string]: string }, src: string }} */
124
126
  let result = { srcset: {}, src: "" }
125
127
 
126
128
  const { outName, remoteName, layout, breakpoints, resolutions, format } =
@@ -160,7 +162,7 @@ export function getPatternAttrs(optimize, recipe, view, resizeOnly) {
160
162
  remoteName,
161
163
  width,
162
164
  height,
163
- format
165
+ format,
164
166
  )
165
167
 
166
168
  if (!resizeOnly) {
@@ -2,7 +2,7 @@ import { imageSizeFromFile } from "image-size/fromFile"
2
2
 
3
3
  /**
4
4
  * @param {string} fullPath
5
- * @returns {Promise<{ width: number, height: number } | null>}
5
+ * @returns {Promise<{ width: number, height: number }>}
6
6
  */
7
7
  export async function getSize(fullPath) {
8
8
  try {
@@ -10,6 +10,6 @@ export async function getSize(fullPath) {
10
10
  return { width, height }
11
11
  } catch (err) {
12
12
  console.error(`Failed to get image size for ${fullPath}`, err)
13
- return null
13
+ return { width: 0, height: 0 }
14
14
  }
15
15
  }
@@ -1,5 +1,5 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
2
- /** @typedef {import('rolldown-vite').ViteDevServer} ViteDevServer */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
+ /** @typedef {import('vite').ViteDevServer} ViteDevServer */
3
3
  /** @typedef {import('./types').PluginOptions} PluginOptions */
4
4
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
5
5
  /** @typedef {import('../ssg/types').SsgPage} SsgPage */
@@ -81,7 +81,7 @@ export function pluginIsland(uOpts = {}) {
81
81
  enforce: "pre",
82
82
  apply(_, { command, isSsrBuild }) {
83
83
  isDev = command === "serve"
84
- isSsr = command === "build" && isSsrBuild
84
+ isSsr = command === "build" && Boolean(isSsrBuild)
85
85
  isBuild = command === "build" && !isSsrBuild
86
86
  return isDev || isSsr || isBuild
87
87
  },
@@ -293,8 +293,9 @@ export function pluginIsland(uOpts = {}) {
293
293
  for (const item of Object.values(outputChunks)) {
294
294
  if (item.name !== entryId) continue
295
295
  if (!item.code.trim()) continue
296
+ if (!entryId) continue
296
297
 
297
- const patternIndex = entryId.match(/(\d+)(?!.*\d)/)[0] || "1"
298
+ const patternIndex = entryId.match(/(\d+)(?!.*\d)/)?.[0] || "1"
298
299
  const newFileName = item.fileName
299
300
  entryChanges[patternIndex] = newFileName
300
301
 
@@ -40,6 +40,7 @@ function printNode(node) {
40
40
  * @returns {{[key: string]: any}}
41
41
  */
42
42
  function evalObjectExpr(objExpr) {
43
+ /** @type {{[key: string]: any}} */
43
44
  let out = {}
44
45
  for (const prop of objExpr.properties) {
45
46
  if (prop.type !== "KeyValueProperty") continue
@@ -64,6 +65,7 @@ export function transformDirectives(code, id, opts) {
64
65
  const prefix = rootAttrName ? `${rootAttrName}-` : ""
65
66
  const ast = parseSync(code, { syntax: "typescript", tsx: true })
66
67
 
68
+ /** @type {{[key: string]: any}} */
67
69
  let importMap = {}
68
70
 
69
71
  for (const node of ast.body) {
@@ -107,10 +109,11 @@ export function transformDirectives(code, id, opts) {
107
109
  const opening = node.opening
108
110
  if (opening && Array.isArray(opening.attributes)) {
109
111
  const idx = opening.attributes.findIndex(
112
+ /** @param {any} attr */
110
113
  (attr) =>
111
114
  attr.type === "JSXAttribute" &&
112
115
  attr.name.type === "JSXNamespacedName" &&
113
- attr.name.namespace.value === "client"
116
+ attr.name.namespace.value === "client",
114
117
  )
115
118
  if (idx !== -1) {
116
119
  const [directive] = opening.attributes.splice(idx, 1)
@@ -121,22 +124,25 @@ export function transformDirectives(code, id, opts) {
121
124
 
122
125
  if (clientName === "only") {
123
126
  const fallback = (node.children || []).find(
127
+ /** @param {any} c */
124
128
  (c) =>
125
129
  c.type === "JSXElement" &&
126
130
  Array.isArray(c.opening.attributes) &&
127
131
  c.opening.attributes.some(
132
+ /** @param {any} a */
128
133
  (a) =>
129
134
  a.type === "JSXAttribute" &&
130
135
  a.name.value === "slot" &&
131
- a.value?.value === "fallback"
132
- )
136
+ a.value?.value === "fallback",
137
+ ),
133
138
  )
134
139
  if (fallback) {
135
140
  ssrNode = fallback
136
141
 
137
142
  const { opening: origOpening, children: origChildren } = node
138
143
  const filteredChildren = (origChildren || []).filter(
139
- (c) => c !== fallback
144
+ /** @param {any} c */
145
+ (c) => c !== fallback,
140
146
  )
141
147
  snippetNode = {
142
148
  ...node,
@@ -158,6 +164,10 @@ export function transformDirectives(code, id, opts) {
158
164
  let rawJsx = printNode(snippetNode).replace(/;?\s*$/, "")
159
165
  let tagNames = new Set()
160
166
 
167
+ /**
168
+ * @param {*} n
169
+ * @returns {void}
170
+ */
161
171
  function collect(n) {
162
172
  if (!n || typeof n !== "object") return
163
173
  if (n.type === "JSXElement") {
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
 
@@ -64,30 +64,32 @@ export function Search(props) {
64
64
 
65
65
  const [inputValue, setInputValue] = useState("")
66
66
 
67
- /** @type {import("react").RefObject<HTMLInputElement>} */
67
+ /** @type {React.RefObject<HTMLInputElement|null>} */
68
68
  const inputRef = useRef(null)
69
69
 
70
- /** @type {[SearchData, React.Dispatch<React.SetStateAction<SearchData>>]} */
71
- const [searchData, setSearchData] = useState({
70
+ /** @type {SearchData} */
71
+ const defaultSearchData = {
72
72
  words: [],
73
73
  hits: [],
74
74
  pages: [],
75
- })
76
-
77
- /** @type {[string[], React.Dispatch<React.SetStateAction<string[]>>]} */
78
- const [searchHits, setSearchHits] = useState([])
79
-
80
- /** @type {[SearchPage[], React.Dispatch<React.SetStateAction<SearchPage[]>>]} */
81
- const [searchPages, setSearchPages] = useState([])
82
-
83
- /** @type {[string[], React.Dispatch<React.SetStateAction<string[]>>]} */
84
- const [searchValues, setSearchValues] = useState([])
85
-
86
- /** @type {[string[], React.Dispatch<React.SetStateAction<string[]>>]} */
87
- const [searchHitValues, setSearchHitValues] = useState([])
88
-
89
- /** @type {[SearchResult[], React.Dispatch<React.SetStateAction<SearchResult[]>>]} */
90
- const [searchResults, setSearchResults] = useState([])
75
+ }
76
+ /** @type {string[]} */
77
+ const defaultSearchHits = []
78
+ /** @type {SearchPage[]} */
79
+ const defaultSearchPages = []
80
+ /** @type {string[]} */
81
+ const defaultSearchValues = []
82
+ /** @type {string[]} */
83
+ const defaultSearchHitValues = []
84
+ /** @type {SearchResult[]} */
85
+ const defaultSearchResults = []
86
+
87
+ const [searchData, setSearchData] = useState(defaultSearchData)
88
+ const [searchHits, setSearchHits] = useState(defaultSearchHits)
89
+ const [searchPages, setSearchPages] = useState(defaultSearchPages)
90
+ const [searchValues, setSearchValues] = useState(defaultSearchValues)
91
+ const [searchHitValues, setSearchHitValues] = useState(defaultSearchHitValues)
92
+ const [searchResults, setSearchResults] = useState(defaultSearchResults)
91
93
 
92
94
  const checkValues = searchValues && searchValues.length
93
95
  const checkHitValues = searchHitValues && searchHitValues.length
@@ -107,18 +109,18 @@ export function Search(props) {
107
109
  const hitValues = mergedInputValues.flatMap((value) =>
108
110
  value.length >= minHitLength
109
111
  ? searchHits.filter((hit) => new RegExp(value, "i").test(hit))
110
- : []
112
+ : [],
111
113
  )
112
114
  const mergedHitValues = [...new Set(hitValues)].sort()
113
115
  const hitIndexes = mergedHitValues.map((value) =>
114
- searchData.words.indexOf(value)
116
+ searchData.words.indexOf(value),
115
117
  )
116
118
 
117
119
  /** @type {SearchPage[]} */
118
120
  const hitPages = searchPages.flatMap((page) => {
119
121
  const titleIndexs = page.title.filter((i) => hitIndexes.indexOf(i) !== -1)
120
122
  const contentIndexs = page.content.filter(
121
- (i) => hitIndexes.indexOf(i) !== -1
123
+ (i) => hitIndexes.indexOf(i) !== -1,
122
124
  )
123
125
  if (titleIndexs.length || contentIndexs.length) {
124
126
  return {
@@ -142,39 +144,48 @@ export function Search(props) {
142
144
  })
143
145
  .slice(0, maxHitPages)
144
146
 
145
- const resultHitPages = sortedHitPages.map((page) => {
146
- const targetPage = searchData.pages.find(
147
- (dataPage) => dataPage.url === page.url
148
- )
149
- if (page.title.length) {
150
- const targetContent = targetPage.title
151
- .map((num) => searchData.words[num])
152
- .join(" ")
153
- return { url: targetPage.url, content: targetContent }
154
- } else {
155
- const targetWord = page.content[0]
156
- const targetIndex = targetPage.content.indexOf(targetWord)
157
- const targetIndexes = targetPage.content.slice(
158
- targetIndex,
159
- targetIndex + maxHitWords
160
- )
161
- const targetWords = joinTokens(
162
- targetIndexes.map((num) => searchData.words[num])
147
+ const resultHitPages = sortedHitPages
148
+ .map((page) => {
149
+ const targetPage = searchData.pages.find(
150
+ (dataPage) => dataPage.url === page.url,
163
151
  )
164
- const targetContent = "..." + targetWords + "..."
165
- const targetId = () => {
166
- if (!page.toc.length) return ""
167
- const targetToc = page.toc
168
- .filter((item) => targetIndex >= item[0])
169
- .slice(-1)[0]
170
- return targetToc ? "#" + targetToc[1] : ""
152
+ if (!targetPage) return null
153
+ if (page.title.length) {
154
+ const targetContent = targetPage?.title
155
+ .map((num) => searchData.words[num])
156
+ .join(" ")
157
+ return { url: targetPage.url, content: targetContent }
158
+ } else {
159
+ const targetWord = page.content[0]
160
+ const targetIndex = targetPage.content.indexOf(targetWord)
161
+ const targetIndexes = targetPage.content.slice(
162
+ targetIndex,
163
+ targetIndex + maxHitWords,
164
+ )
165
+ const targetWords = joinTokens(
166
+ targetIndexes.map((num) => searchData.words[num]),
167
+ )
168
+ const targetContent = "..." + targetWords + "..."
169
+ const targetId = () => {
170
+ if (!page.toc.length) return ""
171
+ const targetToc = page.toc
172
+ .filter((item) => targetIndex >= item[0])
173
+ .slice(-1)[0]
174
+ return targetToc ? "#" + targetToc[1] : ""
175
+ }
176
+ return { url: targetPage.url + targetId(), content: targetContent }
171
177
  }
172
- return { url: targetPage.url + targetId(), content: targetContent }
173
- }
174
- })
178
+ })
179
+ .filter(
180
+ /**
181
+ * @param {SearchResult | null} file
182
+ * @returns {file is SearchResult}
183
+ */
184
+ (file) => Boolean(file),
185
+ )
175
186
 
176
187
  setSearchValues(
177
- mergedInputValues.filter((value) => value && !value.includes(" "))
188
+ mergedInputValues.filter((value) => value && !value.includes(" ")),
178
189
  )
179
190
  setSearchHitValues(mergedHitValues)
180
191
  setSearchResults(resultHitPages)
@@ -232,8 +243,8 @@ export function Search(props) {
232
243
  glyphs.map((glyph, glyphIndex) =>
233
244
  glyph.match(regValues)
234
245
  ? createElement("mark", { key: glyphIndex }, glyph)
235
- : createElement("span", { key: glyphIndex }, glyph)
236
- )
246
+ : createElement("span", { key: glyphIndex }, glyph),
247
+ ),
237
248
  )
238
249
  } else {
239
250
  return createElement("span", { key: wordIndex }, word)
@@ -268,7 +279,7 @@ export function Search(props) {
268
279
 
269
280
  if (apply === "build") {
270
281
  const el = document.querySelector(`[${relativeAttr}]`)
271
- const distance = Number(el.getAttribute(relativeAttr)) || 0
282
+ const distance = Number(el?.getAttribute(relativeAttr)) || 0
272
283
  const here = location.pathname
273
284
  let segments = here.split("/").filter(Boolean)
274
285
  distance > 0 && (segments = segments.slice(0, -distance))
@@ -301,16 +312,18 @@ export function Search(props) {
301
312
 
302
313
  return createElement(
303
314
  "div",
304
- Object.assign({}, attributes, className ? { className } : {}, wrapperRest),
305
-
315
+ {
316
+ ...attributes,
317
+ ...(className ? { className } : {}),
318
+ ...wrapperRest,
319
+ },
306
320
  createElement(
307
321
  "div",
308
- Object.assign(
309
- {},
310
- fieldAttributes,
311
- fieldClassName ? { className: fieldClassName } : {},
312
- fieldRest
313
- ),
322
+ {
323
+ ...fieldAttributes,
324
+ ...(fieldClassName ? { className: fieldClassName } : {}),
325
+ ...fieldRest,
326
+ },
314
327
  beforeElement || null,
315
328
  createElement("input", {
316
329
  type: "search",
@@ -321,18 +334,17 @@ export function Search(props) {
321
334
  ref: inputRef,
322
335
  }),
323
336
  resolvedClearElement,
324
- afterElement || null
337
+ afterElement || null,
325
338
  ),
326
339
 
327
340
  checkResults
328
341
  ? createElement(
329
342
  "ul",
330
- Object.assign(
331
- {},
332
- listAttributes,
333
- listClassName ? { className: listClassName } : {},
334
- listRest
335
- ),
343
+ {
344
+ ...listAttributes,
345
+ ...(listClassName ? { className: listClassName } : {}),
346
+ ...listRest,
347
+ },
336
348
  searchResults.map((item, index) => {
337
349
  const url = basedUrl(item.url)
338
350
  return createElement(
@@ -347,20 +359,20 @@ export function Search(props) {
347
359
  createElement(
348
360
  "p",
349
361
  null,
350
- createElement("strong", null, highlight(item.content))
362
+ createElement("strong", null, highlight(item.content)),
351
363
  ),
352
364
  showUrl
353
365
  ? createElement(
354
366
  "p",
355
367
  null,
356
- createElement("small", null, url)
368
+ createElement("small", null, url),
357
369
  )
358
- : null
359
- )
360
- )
370
+ : null,
371
+ ),
372
+ ),
361
373
  )
362
- })
374
+ }),
363
375
  )
364
- : null
376
+ : null,
365
377
  )
366
378
  }
@@ -1,5 +1,5 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
2
- /** @typedef {import('rolldown-vite').ViteDevServer} ViteDevServer */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
+ /** @typedef {import('vite').ViteDevServer} ViteDevServer */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
  /** @typedef {import('./types').PluginOptions} PluginOptions */
5
5
  /** @typedef {import('../ssg/types').SsgPage} SsgPage */
@@ -15,7 +15,11 @@ import { getSearchData } from "./utils/data.js"
15
15
  import { mergeObj } from "../../shared/obj.js"
16
16
  import { getRootDir, getTempDir } from "../../shared/path.js"
17
17
  import { getServeBase, getBuildBase } from "../../shared/url.js"
18
- import { filterOutputAssets, filterOutputChunks } from "../../shared/vite.js"
18
+ import {
19
+ mergeSsrNoExternal,
20
+ filterOutputAssets,
21
+ filterOutputChunks,
22
+ } from "../../shared/vite.js"
19
23
 
20
24
  const __filename = fileURLToPath(import.meta.url)
21
25
  const __dirname = path.dirname(__filename)
@@ -72,7 +76,7 @@ export function pluginSearch(uOpts = {}) {
72
76
  enforce: "pre",
73
77
  apply(_, { command, isSsrBuild }) {
74
78
  isDev = command === "serve"
75
- isSsr = command === "build" && isSsrBuild
79
+ isSsr = command === "build" && Boolean(isSsrBuild)
76
80
  isBuild = command === "build" && !isSsrBuild
77
81
  return isDev || isBuild
78
82
  },
@@ -82,6 +86,18 @@ export function pluginSearch(uOpts = {}) {
82
86
 
83
87
  if (isDev) {
84
88
  base = getServeBase(config.base || base)
89
+ return {
90
+ ssr: {
91
+ noExternal: mergeSsrNoExternal(config, ["minista"]),
92
+ },
93
+ }
94
+ }
95
+ if (isSsr) {
96
+ return {
97
+ ssr: {
98
+ noExternal: mergeSsrNoExternal(config, ["minista"]),
99
+ },
100
+ }
85
101
  }
86
102
  if (isBuild) {
87
103
  base = getBuildBase(config.base || base)
@@ -195,7 +211,7 @@ export function pluginSearch(uOpts = {}) {
195
211
 
196
212
  const slashCount = (htmlName.match(/\//g) || []).length
197
213
  const bodyEl = parsedHtml.querySelector("body")
198
- bodyEl.setAttribute(opts.relativeAttr, String(slashCount))
214
+ bodyEl?.setAttribute(opts.relativeAttr, String(slashCount))
199
215
 
200
216
  item.source = parsedHtml.toString()
201
217
  }
@@ -207,7 +223,7 @@ export function pluginSearch(uOpts = {}) {
207
223
  return item.originalFileNames.some((name) => name === before)
208
224
  })
209
225
  if (afterItem) {
210
- const oldPath = path.resolve(options.dir, afterItem.fileName)
226
+ const oldPath = path.resolve(options.dir || "", afterItem.fileName)
211
227
  const newPath = oldPath.replace(/\.txt$/, ".json")
212
228
  await fs.promises.rename(oldPath, newPath)
213
229
  }
@@ -151,6 +151,7 @@ function extractPage(pageEl, ignoreSelectors) {
151
151
  }
152
152
  }
153
153
  if (el.childNodes) {
154
+ /* @ts-ignore */
154
155
  el.childNodes.forEach(walk)
155
156
  }
156
157
  }
@@ -217,7 +218,7 @@ export function getSearchData(ssgPages, opts) {
217
218
  const regHits = new RegExp(`(${regHitsArray.join("|")})`)
218
219
  const hitWords = result.words.filter(
219
220
  (word) =>
220
- word.length >= hit.minLength && regHits.test(word) && word !== "..."
221
+ word.length >= hit.minLength && regHits.test(word) && word !== "...",
221
222
  )
222
223
  result.hits = hitWords.map((word) => result.words.indexOf(word))
223
224
 
@@ -1,5 +1,5 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
2
- /** @typedef {import('rolldown-vite').ViteDevServer} ViteDevServer */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
+ /** @typedef {import('vite').ViteDevServer} ViteDevServer */
3
3
  /** @typedef {import('./types').PluginOptions} PluginOptions */
4
4
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
5
5
  /** @typedef {import('../ssg/types').SsgPage} SsgPage */
@@ -83,7 +83,7 @@ export function pluginSprite(uOpts = {}) {
83
83
  enforce: "pre",
84
84
  apply(_, { command, isSsrBuild }) {
85
85
  isDev = command === "serve"
86
- isSsr = command === "build" && isSsrBuild
86
+ isSsr = command === "build" && Boolean(isSsrBuild)
87
87
  isBuild = command === "build" && !isSsrBuild
88
88
  return isDev || isBuild
89
89
  },
@@ -206,7 +206,7 @@ export function pluginSprite(uOpts = {}) {
206
206
  if (!targetEls.length) return html
207
207
 
208
208
  for (const el of targetEls) {
209
- const assetName = el.getAttribute(srcAttr).replace(/^\//, "")
209
+ const assetName = el?.getAttribute(srcAttr)?.replace(/^\//, "") || ""
210
210
  const symbolId =
211
211
  el.getAttribute(symbolIdAttr) || path.parse(assetName).name
212
212
  const assetUrl = assetMap[assetName]
@@ -247,7 +247,7 @@ export function pluginSprite(uOpts = {}) {
247
247
  if (!targetEls.length) continue
248
248
 
249
249
  for (const el of targetEls) {
250
- const assetName = el.getAttribute(srcAttr).replace(/^\//, "")
250
+ const assetName = el?.getAttribute(srcAttr)?.replace(/^\//, "") || ""
251
251
  const symbolId =
252
252
  el.getAttribute(symbolIdAttr) || path.parse(assetName).name
253
253
  const before = spriteMap[assetName]
@@ -39,8 +39,8 @@ export async function generateSprite(targetDir, config) {
39
39
  const doc = parseHtml(code)
40
40
  const el = doc.querySelector("svg")
41
41
  const id = path.parse(svgName).name
42
- const viewBox = el.getAttribute("viewBox")
43
- const { data: content } = optimize(el.innerHTML, config)
42
+ const viewBox = el?.getAttribute("viewBox")
43
+ const { data: content } = optimize(el?.innerHTML || "", config)
44
44
  if (!id || !viewBox || !content) continue
45
45
  symbols[id] = { viewBox, content }
46
46
  }
@@ -66,7 +66,7 @@ export async function generateSprite(targetDir, config) {
66
66
  symbolList
67
67
  .map(
68
68
  ({ id, viewBox, content }) =>
69
- `<symbol id="${id}" viewBox="${viewBox}">${content}</symbol>`
69
+ `<symbol id="${id}" viewBox="${viewBox}">${content}</symbol>`,
70
70
  )
71
71
  .join("\n"),
72
72
  `</svg>`,
@@ -18,6 +18,7 @@ export function Head({
18
18
  }) {
19
19
  /** @type {{ setHeadData?: SetHeadData }} */
20
20
  const { setHeadData } = useContext(HeadContext)
21
+ if (!setHeadData) return null
21
22
 
22
23
  if (htmlAttributes) setHeadData("htmlAttributes", htmlAttributes)
23
24
  if (bodyAttributes) setHeadData("bodyAttributes", bodyAttributes)
@@ -56,6 +57,6 @@ export function HeadProvider({ headData, children }) {
56
57
  return createElement(
57
58
  HeadContext.Provider,
58
59
  { value: { setHeadData } },
59
- children
60
+ children,
60
61
  )
61
62
  }
@@ -1,5 +1,5 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
2
- /** @typedef {import('rolldown-vite').EnvironmentModuleNode} EnvModuleNode */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
+ /** @typedef {import('vite').EnvironmentModuleNode} EnvModuleNode */
3
3
  /** @typedef {import('./types').PluginOptions} PluginOptions */
4
4
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
5
5
  /** @typedef {import('./types').SsgPage} SsgPage */
@@ -60,20 +60,24 @@ export function pluginSsg(uOpts = {}) {
60
60
  * @param {ResolvedPage[]} resolvedPages
61
61
  */
62
62
  function selfUpdateResolvedToSsgPages(resolvedLayout, resolvedPages) {
63
- ssgPages = resolvedPages.map((resolvedPage) => {
64
- if (resolvedPage.metadata?.draft === true) {
65
- return null
66
- }
67
- const url = resolvedPage.url
68
- const fileName = getHtmlFileName(url)
69
- const html = transformHtml({ resolvedLayout, resolvedPage })
70
- return {
71
- url,
72
- fileName,
73
- html,
74
- }
75
- })
76
- ssgPages = ssgPages.filter(Boolean)
63
+ ssgPages = resolvedPages
64
+ .map((resolvedPage) => {
65
+ if (resolvedPage.metadata?.draft === true) {
66
+ return null
67
+ }
68
+ const url = resolvedPage.url
69
+ const fileName = getHtmlFileName(url)
70
+ const html = transformHtml({ resolvedLayout, resolvedPage })
71
+ return {
72
+ url,
73
+ fileName,
74
+ html,
75
+ }
76
+ })
77
+ .filter(
78
+ /** @type {(page: SsgPage | null) => page is SsgPage} */
79
+ (page) => page !== null,
80
+ )
77
81
  }
78
82
 
79
83
  return {
@@ -81,7 +85,7 @@ export function pluginSsg(uOpts = {}) {
81
85
  enforce: "pre",
82
86
  apply(_, { command, isSsrBuild }) {
83
87
  isDev = command === "serve"
84
- isSsr = command === "build" && isSsrBuild
88
+ isSsr = command === "build" && Boolean(isSsrBuild)
85
89
  isBuild = command === "build" && !isSsrBuild
86
90
  return isDev || isSsr || isBuild
87
91
  },
@@ -204,7 +208,7 @@ export function pluginSsg(uOpts = {}) {
204
208
  next()
205
209
  }
206
210
  } catch (e) {
207
- server.ssrFixStacktrace(e)
211
+ if (e instanceof Error) server.ssrFixStacktrace(e)
208
212
  next(e)
209
213
  }
210
214
  })
@@ -219,7 +223,7 @@ export function pluginSsg(uOpts = {}) {
219
223
  * @param {string | undefined | null} id
220
224
  * @returns {string | undefined}
221
225
  */
222
- const stripQuery = (id) => (id ? id.split("?")[0] : id)
226
+ const stripQuery = (id) => (id ? id.split("?")[0] : undefined)
223
227
 
224
228
  /**
225
229
  * @param {EnvModuleNode | null | undefined} mod
@@ -295,7 +299,7 @@ export function pluginSsg(uOpts = {}) {
295
299
  const invalidated = new Set()
296
300
  const clientGraph = server.environments.client.moduleGraph
297
301
 
298
- const isKnownInClient = (mod) => {
302
+ const isKnownInClient = (/** @type {EnvModuleNode} */ mod) => {
299
303
  if (!mod?.id) return false
300
304
  if (clientGraph.getModuleById(mod.id)) return true
301
305
  const file = mod.file ?? stripQuery(mod.id)
@@ -317,7 +321,7 @@ export function pluginSsg(uOpts = {}) {
317
321
 
318
322
  if (hasSsrOnly) {
319
323
  const rel = stripQuery(
320
- path.relative(server.config.root, modules[0].id),
324
+ path.relative(server.config.root, modules[0].id || ""),
321
325
  )
322
326
  server.config.logger.info(
323
327
  [pc.dim("(ssr)"), pc.green("page reload"), pc.dim(rel)].join(" "),
@@ -102,4 +102,4 @@ export type HeadProps = HeadData & {
102
102
  children?: React.ReactNode
103
103
  }
104
104
 
105
- export type SetHeadData = (key: string, value: any) => void
105
+ export type SetHeadData = (key: keyof HeadData, value: any) => void
@@ -30,6 +30,6 @@ export async function resolveLayout(layout) {
30
30
  return {
31
31
  component: layout.component,
32
32
  staticData,
33
- metadata: layout.metadata,
33
+ metadata: layout.metadata ?? {},
34
34
  }
35
35
  }
@@ -1,3 +1,5 @@
1
+ import { createElement } from "react"
2
+
1
3
  /**
2
4
  * @param {string | undefined} title
3
5
  * @param {boolean} hasCharset
@@ -5,30 +7,24 @@
5
7
  * @returns {React.ReactElement[]}
6
8
  */
7
9
  export function getDefaultHeadTags(title, hasCharset, hasViewport) {
8
- return [
9
- !hasCharset && {
10
- type: "meta",
11
- key: null,
12
- props: {
13
- charSet: "UTF-8",
14
- },
15
- },
16
- !hasViewport && {
17
- type: "meta",
18
- key: null,
19
- props: {
10
+ /** @type {React.ReactElement[]} */
11
+ const tags = []
12
+
13
+ if (!hasCharset) {
14
+ tags.push(createElement("meta", { charSet: "UTF-8" }))
15
+ }
16
+ if (!hasViewport) {
17
+ tags.push(
18
+ createElement("meta", {
20
19
  name: "viewport",
21
20
  content: "width=device-width, initial-scale=1.0",
22
- },
23
- },
24
- title && {
25
- type: "title",
26
- key: null,
27
- props: {
28
- children: title,
29
- },
30
- },
31
- ].filter(Boolean)
21
+ }),
22
+ )
23
+ }
24
+ if (title) {
25
+ tags.push(createElement("title", null, title))
26
+ }
27
+ return tags
32
28
  }
33
29
 
34
30
  /**
@@ -52,6 +48,7 @@ export function filterHeadTags(tags) {
52
48
  */
53
49
  export function headTagToStr(tag) {
54
50
  const selfClosingTags = new Set(["link", "meta"])
51
+ /** @type {{ [key: string]: string }} */
55
52
  const attrNameMap = { charSet: "charset" }
56
53
 
57
54
  const tagName = typeof tag?.type === "string" ? tag.type : ""
@@ -1,4 +1,4 @@
1
- /** @typedef {import('rolldown-vite').Plugin} Plugin */
1
+ /** @typedef {import('vite').Plugin} Plugin */
2
2
  /** @typedef {import('./types').PluginOptions} PluginOptions */
3
3
  /** @typedef {import('./types').UserPluginOptions} UserPluginOptions */
4
4
 
@@ -44,7 +44,7 @@ export function pluginSvg(uOpts = {}) {
44
44
  if (!targetEls.length) return html
45
45
 
46
46
  for (const el of targetEls) {
47
- const svgName = el.getAttribute(srcAttr).replace(/^\//, "") ?? ""
47
+ const svgName = el?.getAttribute(srcAttr)?.replace(/^\//, "") ?? ""
48
48
  if (!svgName) continue
49
49
 
50
50
  let svgData = svgObj[svgName]
@@ -86,7 +86,7 @@ export function pluginSvg(uOpts = {}) {
86
86
  enforce: "pre",
87
87
  apply(_, { command, isSsrBuild }) {
88
88
  isDev = command === "serve"
89
- isSsr = command === "build" && isSsrBuild
89
+ isSsr = command === "build" && Boolean(isSsrBuild)
90
90
  isBuild = command === "build" && !isSsrBuild
91
91
  return isDev || isBuild
92
92
  },
package/src/shared/obj.js CHANGED
@@ -9,7 +9,7 @@ export function cleanObj(obj) {
9
9
  return Object.fromEntries(
10
10
  Object.entries(obj)
11
11
  .filter(([, v]) => v !== undefined)
12
- .map(([k, v]) => [k, cleanObj(v)])
12
+ .map(([k, v]) => [k, cleanObj(v)]),
13
13
  )
14
14
  }
15
15
  return obj
@@ -28,7 +28,7 @@ export function mergeObj(obj1, obj2) {
28
28
 
29
29
  for (const key in obj2) {
30
30
  if (
31
- obj2.hasOwnProperty(key) &&
31
+ Object.prototype.hasOwnProperty.call(obj2, key) &&
32
32
  typeof obj2[key] === "object" &&
33
33
  obj2[key] !== null &&
34
34
  !Array.isArray(obj2[key])
@@ -1,5 +1,6 @@
1
- /** @typedef {import('rolldown-vite').UserConfig} UserConfig */
2
- /** @typedef {import('rolldown-vite').Alias} Alias */
1
+ /** @typedef {import('vite').UserConfig} UserConfig */
2
+ /** @typedef {import('vite').SSROptions} SSROptions */
3
+ /** @typedef {import('vite').Alias} Alias */
3
4
  /** @typedef {import('rolldown').OutputBundle} OutputBundle */
4
5
  /** @typedef {import('rolldown').OutputAsset} OutputAsset */
5
6
  /** @typedef {import('rolldown').OutputChunk} OutputChunk */
@@ -7,11 +8,14 @@
7
8
  /**
8
9
  * @param {UserConfig} config
9
10
  * @param {string[]} modules
10
- * @returns {UserConfig['ssr']['external']}
11
+ * @returns {SSROptions['external']}
11
12
  */
12
13
  export function mergeSsrExternal(config, modules = []) {
13
14
  const ssrExternal = config.ssr?.external
14
15
 
16
+ if (ssrExternal === true) {
17
+ return true
18
+ }
15
19
  if (typeof ssrExternal === "undefined") {
16
20
  return modules
17
21
  }
@@ -21,10 +25,30 @@ export function mergeSsrExternal(config, modules = []) {
21
25
  return ssrExternal
22
26
  }
23
27
 
28
+ /**
29
+ * @param {UserConfig} config
30
+ * @param {string[]} modules
31
+ * @returns {SSROptions['noExternal']}
32
+ */
33
+ export function mergeSsrNoExternal(config, modules = []) {
34
+ const noExternal = config.ssr?.noExternal
35
+
36
+ if (noExternal === true) {
37
+ return true
38
+ }
39
+ if (typeof noExternal === "undefined") {
40
+ return modules
41
+ }
42
+ if (Array.isArray(noExternal)) {
43
+ return Array.from(new Set([...noExternal, ...modules]))
44
+ }
45
+ return noExternal
46
+ }
47
+
24
48
  /**
25
49
  * @param {UserConfig} config
26
50
  * @param {Alias[]} aliases
27
- * @returns {UserConfig['resolve']['alias']}
51
+ * @returns {Alias[]}
28
52
  */
29
53
  export function mergeAlias(config, aliases = []) {
30
54
  const existing = config.resolve?.alias
@@ -32,11 +56,11 @@ export function mergeAlias(config, aliases = []) {
32
56
  let list = Array.isArray(existing)
33
57
  ? [...existing]
34
58
  : existing && typeof existing === "object"
35
- ? Object.entries(existing).map(([find, replacement]) => ({
36
- find,
37
- replacement,
38
- }))
39
- : []
59
+ ? Object.entries(existing).map(([find, replacement]) => ({
60
+ find,
61
+ replacement,
62
+ }))
63
+ : []
40
64
 
41
65
  const findSet = new Set(list.map((item) => item.find))
42
66