@newlogic-digital/core 4.1.6 → 4.1.8

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 CHANGED
@@ -12,6 +12,7 @@ import heroicons from '@newlogic-digital/vite-plugin-heroicons'
12
12
  import { fileURLToPath } from 'node:url'
13
13
  import { processPostcssCustomProperties } from './src/postcssCustomProperties.js'
14
14
  import { fontsManifest } from './src/fontsManifest.js'
15
+ import { createCustomProvider } from './src/fontsCustomProvider.js'
15
16
  import { createLogger } from 'vite'
16
17
  import console from 'node:console'
17
18
 
@@ -45,6 +46,7 @@ const defaultOptions = {
45
46
  cert: 'localhost',
46
47
  format: ['latte'],
47
48
  codeSplitting: undefined,
49
+ preloadRemover: false,
48
50
  input: {
49
51
  assets: [
50
52
  './src/styles/*.{css,pcss,scss,sass,less,styl,stylus}',
@@ -78,6 +80,7 @@ const defaultOptions = {
78
80
  tailwindcss: {},
79
81
  send: {},
80
82
  fontless: {
83
+ customProvider: undefined,
81
84
  options: undefined,
82
85
  manifest: undefined,
83
86
  },
@@ -112,6 +115,23 @@ const plugin = async (options = {}) => {
112
115
  */
113
116
  const optionalPlugins = []
114
117
 
118
+ if (options.preloadRemover || options.experimental) {
119
+ optionalPlugins.push({
120
+ name: '@newlogic-digital/preload-remover',
121
+ configResolved(config) {
122
+ if (config.command !== 'build') return
123
+
124
+ const pI = config.plugins.findIndex(
125
+ p => p.name === 'native:import-analysis-build',
126
+ )
127
+
128
+ const plugins = /** @type {import('vite').Plugin[]} */ (config.plugins)
129
+
130
+ if (pI !== -1) plugins.splice(pI, 1)
131
+ },
132
+ })
133
+ }
134
+
115
135
  if (options.format.includes('twig')) {
116
136
  const twig = (await import('@vituum/vite-plugin-twig')).default
117
137
 
@@ -137,6 +157,23 @@ const plugin = async (options = {}) => {
137
157
  if (options.fontless?.options) {
138
158
  const { fontless } = await import('fontless')
139
159
 
160
+ if (!options.fontless.options && options.experimental) {
161
+ options.fontless.options = {
162
+ processCSSVariables: true,
163
+ defaults: { weights: ['400 700'], subsets: ['latin', 'latin-ext'] },
164
+ assets: { prefix: '/assets/fonts' },
165
+ }
166
+ }
167
+
168
+ if (!options.fontless.manifest && options.experimental) {
169
+ options.fontless.manifest = ['Inter']
170
+ }
171
+
172
+ if (Array.isArray(options.fontless.customProvider) && options.fontless.customProvider.length > 0) {
173
+ options.fontless.options.providers ??= {}
174
+ options.fontless.options.providers.custom = createCustomProvider(options.fontless.customProvider)
175
+ }
176
+
140
177
  optionalPlugins.push(fontless(options.fontless.options), fontsManifest(options.fontless.manifest))
141
178
  }
142
179
 
@@ -275,27 +312,52 @@ const plugin = async (options = {}) => {
275
312
  }, userConfig.build.rolldownOptions ?? {})
276
313
  }
277
314
  else {
315
+ /**
316
+ * @type {import('rolldown').CodeSplittingGroup[]}
317
+ */
318
+ let groups = [
319
+ {
320
+ name: 'swup',
321
+ test: /node_modules[\\/]swup(?:[\\/]|$)/,
322
+ priority: 30,
323
+ },
324
+ {
325
+ name: 'hotwired-stimulus',
326
+ test: /node_modules[\\/]@hotwired[\\/]stimulus(?:[\\/]|$)/,
327
+ priority: 30,
328
+ },
329
+ {
330
+ name: 'naja',
331
+ test: /node_modules[\\/]naja(?:[\\/]|$)/,
332
+ priority: 30,
333
+ },
334
+ ]
335
+
336
+ if (options.experimental) {
337
+ groups = [
338
+ {
339
+ name: 'naja',
340
+ test: /node_modules[\\/]naja(?:[\\/]|$)/,
341
+ priority: 30,
342
+ },
343
+ {
344
+ name: 'webuum',
345
+ test: id => [
346
+ /node_modules[\\/]webuum(?:[\\/]|$)/,
347
+ /node_modules[\\/]@newlogic-digital[\\/]utils-js(?:[\\/]|$)/,
348
+ /node_modules[\\/]winduum[\\/]src[\\/]supports\.js$/,
349
+ /node_modules[\\/]winduum[\\/]src[\\/]common\.js$/,
350
+ ].some(pattern => pattern.test(id)),
351
+ priority: 30,
352
+ },
353
+ ]
354
+ }
355
+
278
356
  userConfig.build.rolldownOptions = Object.assign({
279
357
  input: defaultInput,
280
358
  output: {
281
359
  codeSplitting: options.codeSplitting ?? {
282
- groups: [
283
- {
284
- name: 'swup',
285
- test: /node_modules[\\/]swup(?:[\\/]|$)/,
286
- priority: 30,
287
- },
288
- {
289
- name: 'hotwired-stimulus',
290
- test: /node_modules[\\/]@hotwired[\\/]stimulus(?:[\\/]|$)/,
291
- priority: 30,
292
- },
293
- {
294
- name: 'naja',
295
- test: /node_modules[\\/]naja(?:[\\/]|$)/,
296
- priority: 30,
297
- },
298
- ],
360
+ groups,
299
361
  },
300
362
  },
301
363
  }, userConfig.build.rolldownOptions ?? {})
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@newlogic-digital/core",
3
3
  "type": "module",
4
- "version": "4.1.6",
4
+ "version": "4.1.8",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "newlogic-core": "./bin/newlogic-core.js"
@@ -22,13 +22,13 @@
22
22
  "@stylistic/eslint-plugin": "^5.10",
23
23
  "@stylistic/stylelint-config": "^5.0.0",
24
24
  "@vituum/vite-plugin-css-inline": "^2.1.0",
25
- "@vituum/vite-plugin-latte": "^2.0.2",
26
- "@vituum/vite-plugin-send": "^2.0.1",
27
- "browserslist": "^4.28.2",
25
+ "@vituum/vite-plugin-latte": "^2.0.3",
26
+ "@vituum/vite-plugin-send": "^2.1.0",
27
+ "browserslist": "^4.28.4",
28
28
  "browserslist-to-esbuild": "^2.1.1",
29
29
  "lightningcss": "^1.32.0",
30
- "npm-check-updates": "^22.2.0",
31
- "oxlint": "^1.66.0",
30
+ "npm-check-updates": "^22.2.7",
31
+ "oxlint": "^1.71.0",
32
32
  "postcss-custom-properties": "^15.0.1",
33
33
  "stylelint-config-standard": "^40.0.0",
34
34
  "vituum": "^2.0.2"
@@ -51,11 +51,11 @@
51
51
  }
52
52
  },
53
53
  "devDependencies": {
54
- "@tailwindcss/vite": "^4.3.0",
55
- "@types/node": "^25.9",
54
+ "@tailwindcss/vite": "^4.3.1",
55
+ "@types/node": "^26.0",
56
56
  "@vituum/vite-plugin-twig": "^2.0.1",
57
57
  "fontless": "^0.2.1",
58
- "rolldown": "^1.0.2",
58
+ "rolldown": "^1.1.3",
59
59
  "typescript": "^6"
60
60
  },
61
61
  "files": [
@@ -0,0 +1,59 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import process from 'node:process'
4
+ import { defineFontProvider } from 'unifont'
5
+
6
+ /**
7
+ * File extension → CSS format() + MIME for the data URL
8
+ *
9
+ * @type {Record<string, { format: string, mime: string }>}
10
+ */
11
+ const fontFormats = {
12
+ woff2: { format: 'woff2', mime: 'font/woff2' },
13
+ woff: { format: 'woff', mime: 'font/woff' },
14
+ ttf: { format: 'truetype', mime: 'font/ttf' },
15
+ otf: { format: 'opentype', mime: 'font/otf' },
16
+ }
17
+
18
+ /**
19
+ * Reads a font file and returns it as a base64 data URL
20
+ *
21
+ * @param {string} src - font path relative to process.cwd()
22
+ * @returns {{ url: string, format: string }}
23
+ */
24
+ const fontSource = (src) => {
25
+ const extension = src.split('.').pop()?.toLowerCase() ?? ''
26
+ const type = fontFormats[extension]
27
+
28
+ if (!type) {
29
+ throw new Error(`[fontless] Unsupported font extension "${extension}" (${src}). Supported: ${Object.keys(fontFormats).join(', ')}`)
30
+ }
31
+
32
+ const data = readFileSync(resolve(process.cwd(), src)).toString('base64')
33
+
34
+ return { url: `data:${type.mime};base64,${data}`, format: type.format }
35
+ }
36
+
37
+ /**
38
+ * Builds the "custom" unifont provider that base64-inlines the local fonts
39
+ * defined by the `customProvider` array in the fontless configuration
40
+ *
41
+ * @param {Array<{ name: string, fonts: Array<{ src: string, weight?: number | string, style?: string }> }>} entries
42
+ * @returns {import('unifont').ProviderFactory<'custom'>}
43
+ */
44
+ export const createCustomProvider = entries =>
45
+ /** @type {import('unifont').ProviderFactory<'custom'>} */ (defineFontProvider('custom', () => ({
46
+ resolveFont(family) {
47
+ const entry = entries.find(item => item.name === family)
48
+
49
+ if (!entry) return
50
+
51
+ return {
52
+ fonts: entry.fonts.map(font => ({
53
+ src: [fontSource(font.src)],
54
+ weight: font.weight,
55
+ style: font.style,
56
+ })),
57
+ }
58
+ },
59
+ })))
@@ -82,9 +82,10 @@ export const fontsManifest = families => ({
82
82
  if (fonts.length === 0) continue
83
83
 
84
84
  entry.assets = [...new Set([...entry.assets ?? [], ...fonts.map(font => font.file)])]
85
- // rodina → subset → pole souborů; umožňuje preloadovat jen vybrané rodiny a subsety
85
+ // family → subset → array of files; allows preloading only selected families and subsets.
86
+ // Fonts without a detectable subset (e.g. non-subsetted local fonts) fall back to the 'default' key.
86
87
  entry.fonts = fonts.reduce((acc, font) => {
87
- if (font.family && font.subset) ((acc[font.family] ??= {})[font.subset] ??= []).push(font.file)
88
+ if (font.family) ((acc[font.family] ??= {})[font.subset ?? 'default'] ??= []).push(font.file)
88
89
  return acc
89
90
  }, {})
90
91
  hasFonts = true
package/types/index.d.ts CHANGED
@@ -6,16 +6,26 @@ interface Input {
6
6
 
7
7
  export interface PluginUserConfig {
8
8
  mode?: 'development' | 'production' | 'emails' | string
9
+ experimental?: boolean
9
10
  format?: string[]
10
11
  input?: Input
11
12
  cert?: string
12
13
  codeSplitting?: import('rolldown').OutputOptions['codeSplitting']
14
+ preloadRemover?: boolean
13
15
  vituum?: import('vituum').UserConfig
14
16
  css?: import('vite').CSSOptions
15
17
  cssInline?: import('@vituum/vite-plugin-css-inline').PluginUserConfig
16
18
  send?: import('@vituum/vite-plugin-send').PluginUserConfig
17
19
  tailwindcss?: import('@tailwindcss/vite').PluginOptions
18
20
  fontless?: {
21
+ customProvider?: Array<{
22
+ name: string
23
+ fonts: Array<{
24
+ src: string
25
+ weight?: number | string
26
+ style?: string
27
+ }>
28
+ }>
19
29
  options?: import('fontless').FontlessOptions
20
30
  manifest?: string[]
21
31
  }