zyzz 0.0.18 → 0.0.20

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.
Files changed (120) hide show
  1. package/README.md +9 -9
  2. package/dist/Config.d.ts +1 -0
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js.map +1 -1
  5. package/dist/Vars.d.ts +22 -15
  6. package/dist/Vars.d.ts.map +1 -1
  7. package/dist/Vars.js +26 -0
  8. package/dist/Vars.js.map +1 -1
  9. package/dist/cli/index.js +5 -0
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/compiler/Source.d.ts.map +1 -1
  12. package/dist/compiler/Source.js +3 -0
  13. package/dist/compiler/Source.js.map +1 -1
  14. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  15. package/dist/compiler/internal/Configurations.js +3 -0
  16. package/dist/compiler/internal/Configurations.js.map +1 -1
  17. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  18. package/dist/compiler/internal/Contract.js +30 -4
  19. package/dist/compiler/internal/Contract.js.map +1 -1
  20. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  21. package/dist/compiler/internal/Themes.js +83 -23
  22. package/dist/compiler/internal/Themes.js.map +1 -1
  23. package/dist/default.d.ts +7 -11
  24. package/dist/default.d.ts.map +1 -1
  25. package/dist/default.js +4 -5
  26. package/dist/default.js.map +2 -2
  27. package/dist/default.js.zyzz.json +1 -1
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +15 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/internal/Token.d.ts +11 -2
  33. package/dist/internal/Token.d.ts.map +1 -1
  34. package/dist/internal/Token.js +2 -0
  35. package/dist/internal/Token.js.map +1 -1
  36. package/dist/internal/VariableSets.d.ts.map +1 -1
  37. package/dist/internal/VariableSets.js +18 -1
  38. package/dist/internal/VariableSets.js.map +1 -1
  39. package/dist/node/Host.d.ts +2 -0
  40. package/dist/node/Host.d.ts.map +1 -1
  41. package/dist/node/Host.js +7 -0
  42. package/dist/node/Host.js.map +1 -1
  43. package/dist/oxlint/index.d.ts +6 -0
  44. package/dist/oxlint/index.d.ts.map +1 -0
  45. package/dist/oxlint/index.js +18 -0
  46. package/dist/oxlint/index.js.map +1 -0
  47. package/dist/oxlint/internal/Bindings.d.ts +20 -0
  48. package/dist/oxlint/internal/Bindings.d.ts.map +1 -0
  49. package/dist/oxlint/internal/Bindings.js +203 -0
  50. package/dist/oxlint/internal/Bindings.js.map +1 -0
  51. package/dist/oxlint/internal/Declarations.d.ts +19 -0
  52. package/dist/oxlint/internal/Declarations.d.ts.map +1 -0
  53. package/dist/oxlint/internal/Declarations.js +114 -0
  54. package/dist/oxlint/internal/Declarations.js.map +1 -0
  55. package/dist/oxlint/rules/noConflictingProps.d.ts +5 -0
  56. package/dist/oxlint/rules/noConflictingProps.d.ts.map +1 -0
  57. package/dist/oxlint/rules/noConflictingProps.js +45 -0
  58. package/dist/oxlint/rules/noConflictingProps.js.map +1 -0
  59. package/dist/oxlint/rules/noUnused.d.ts +5 -0
  60. package/dist/oxlint/rules/noUnused.d.ts.map +1 -0
  61. package/dist/oxlint/rules/noUnused.js +62 -0
  62. package/dist/oxlint/rules/noUnused.js.map +1 -0
  63. package/dist/oxlint/rules/restrictedProperties.d.ts +5 -0
  64. package/dist/oxlint/rules/restrictedProperties.d.ts.map +1 -0
  65. package/dist/oxlint/rules/restrictedProperties.js +82 -0
  66. package/dist/oxlint/rules/restrictedProperties.js.map +1 -0
  67. package/dist/oxlint/rules/useLogicalProperties.d.ts +5 -0
  68. package/dist/oxlint/rules/useLogicalProperties.d.ts.map +1 -0
  69. package/dist/oxlint/rules/useLogicalProperties.js +76 -0
  70. package/dist/oxlint/rules/useLogicalProperties.js.map +1 -0
  71. package/dist/oxlint/rules/validStyles.d.ts +5 -0
  72. package/dist/oxlint/rules/validStyles.d.ts.map +1 -0
  73. package/dist/oxlint/rules/validStyles.js +75 -0
  74. package/dist/oxlint/rules/validStyles.js.map +1 -0
  75. package/dist/react-native/StyleSheet.js +2 -0
  76. package/dist/react-native/StyleSheet.js.map +1 -1
  77. package/dist/runtime/Composition.d.ts +1 -1
  78. package/dist/runtime/ConditionalRecipe.d.ts +1 -1
  79. package/dist/runtime/PayloadRecipe.d.ts +1 -1
  80. package/dist/runtime/Recipe.d.ts +1 -1
  81. package/dist/styleFunction.d.ts +1 -1
  82. package/dist/styleFunction.d.ts.map +1 -1
  83. package/dist/styleFunction.js.map +1 -1
  84. package/dist/variants.d.ts +14 -6
  85. package/dist/variants.d.ts.map +1 -1
  86. package/dist/variants.js.map +1 -1
  87. package/dist/vite/index.d.ts +2 -0
  88. package/dist/vite/index.d.ts.map +1 -1
  89. package/dist/vite/index.js +75 -30
  90. package/dist/vite/index.js.map +1 -1
  91. package/dist/web/internal/Themes.d.ts.map +1 -1
  92. package/dist/web/internal/Themes.js +4 -0
  93. package/dist/web/internal/Themes.js.map +1 -1
  94. package/package.json +7 -1
  95. package/src/Config.ts +5 -0
  96. package/src/Vars.ts +88 -29
  97. package/src/cli/index.ts +5 -0
  98. package/src/compiler/Source.ts +3 -0
  99. package/src/compiler/internal/Configurations.ts +3 -0
  100. package/src/compiler/internal/Contract.ts +47 -4
  101. package/src/compiler/internal/Themes.ts +109 -34
  102. package/src/default.ts +8 -10
  103. package/src/index.ts +15 -0
  104. package/src/internal/Authoring.ts +1 -1
  105. package/src/internal/Token.ts +39 -11
  106. package/src/internal/VariableSets.ts +25 -1
  107. package/src/node/Host.ts +19 -0
  108. package/src/oxlint/index.ts +21 -0
  109. package/src/oxlint/internal/Bindings.ts +270 -0
  110. package/src/oxlint/internal/Declarations.ts +137 -0
  111. package/src/oxlint/rules/noConflictingProps.ts +52 -0
  112. package/src/oxlint/rules/noUnused.ts +67 -0
  113. package/src/oxlint/rules/restrictedProperties.ts +92 -0
  114. package/src/oxlint/rules/useLogicalProperties.ts +81 -0
  115. package/src/oxlint/rules/validStyles.ts +88 -0
  116. package/src/react-native/StyleSheet.ts +6 -0
  117. package/src/styleFunction.ts +1 -3
  118. package/src/variants.ts +38 -16
  119. package/src/vite/index.ts +94 -33
  120. package/src/web/internal/Themes.ts +4 -0
package/src/vite/index.ts CHANGED
@@ -69,6 +69,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
69
69
  const sourceEntrypoints = new Set<string>()
70
70
  const identities = new Map<string, string>()
71
71
  const lazyImports = new WeakMap<Parser.ParseResult, string[]>()
72
+ let directories: readonly string[]
72
73
  let root: string
73
74
 
74
75
  function snapshot(environment: Environment) {
@@ -94,12 +95,33 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
94
95
  function sourceId(file: string) {
95
96
  let id = identities.get(file)
96
97
  if (!id) {
97
- id = `app/${Path.relative(root, file).split(Path.sep).join('/')}`
98
+ const directory = sourceRoot(file) ?? root
99
+ const namespace =
100
+ directory === root
101
+ ? 'app'
102
+ : `include-${directories.indexOf(directory) - 1}`
103
+ id = `${namespace}/${Path.relative(directory, file).split(Path.sep).join('/')}`
98
104
  identities.set(file, id)
99
105
  }
100
106
  return id
101
107
  }
102
108
 
109
+ // Included directories have portable namespaces rather than traversal IDs.
110
+ function sourceFile(id: string) {
111
+ const offset = id.indexOf('/')
112
+ const namespace = id.slice(0, offset)
113
+ const directory = (() => {
114
+ if (namespace === 'app') return root
115
+ if (/^include-\d+$/.test(namespace))
116
+ return directories[Number(namespace.slice(8)) + 1]
117
+ return undefined
118
+ })()
119
+
120
+ return directory === undefined
121
+ ? undefined
122
+ : Path.join(directory, id.slice(offset + 1))
123
+ }
124
+
103
125
  function resource(id: string) {
104
126
  const query = id.split('?')[1]?.split('#')[0]
105
127
 
@@ -119,17 +141,28 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
119
141
  if (sourceEntrypoints.has(normalize(id))) return true
120
142
  // Ids carrying foreign queries are Vite resources such as inline-module proxies, not source.
121
143
  if (resource(id)) return false
122
- const relative = Path.relative(root, id)
123
144
 
124
145
  return (
125
146
  Path.isAbsolute(id) &&
126
- !relative.startsWith('..') &&
127
- !relative.split(Path.sep).includes('node_modules') &&
147
+ sourceRoot(id) !== undefined &&
128
148
  /\.[cm]?[jt]sx?$/.test(id) &&
129
- !/\.(?:d|test|test-d|bench)\.[cm]?[jt]sx?$/.test(id)
149
+ !/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(id)
130
150
  )
131
151
  }
132
152
 
153
+ function sourceRoot(file: string) {
154
+ return directories.find((directory) => {
155
+ const relative = Path.relative(directory, file)
156
+
157
+ return (
158
+ relative !== '..' &&
159
+ !relative.startsWith(`..${Path.sep}`) &&
160
+ !Path.isAbsolute(relative) &&
161
+ !relative.split(Path.sep).includes('node_modules')
162
+ )
163
+ })
164
+ }
165
+
133
166
  function discover(environment: Environment, host: Host) {
134
167
  let pending = discoveries.get(environment)
135
168
 
@@ -137,10 +170,14 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
137
170
  pending = (async () => {
138
171
  const sources = new Map<string, string>()
139
172
  const eagerFiles = new Set<string>()
173
+ const visited = new Set<string>()
140
174
 
141
175
  contributionFiles.set(environment, eagerFiles)
142
176
 
143
177
  async function collect(directory: string): Promise<void> {
178
+ if (visited.has(directory)) return
179
+ visited.add(directory)
180
+
144
181
  for (const item of await Fs.readdir(directory, {
145
182
  withFileTypes: true,
146
183
  })) {
@@ -163,7 +200,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
163
200
  const file = Path.join(directory, item.name)
164
201
 
165
202
  if (item.isDirectory()) await collect(file)
166
- else if (item.isFile() && eager(file)) {
203
+ else if (item.isFile() && eager(file, environment)) {
167
204
  host.watch(file)
168
205
 
169
206
  const source = await Fs.readFile(file, 'utf8')
@@ -175,7 +212,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
175
212
  }
176
213
  }
177
214
 
178
- await collect(root)
215
+ for (const directory of directories) await collect(directory)
179
216
 
180
217
  return sources
181
218
  })()
@@ -185,10 +222,17 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
185
222
  return pending
186
223
  }
187
224
 
188
- function eager(file: string) {
225
+ function eager(file: string, environment: Environment) {
189
226
  return (
190
227
  eligible(file) &&
191
- !Path.relative(root, file)
228
+ file !== environment.config.configFile &&
229
+ !environment.config.configFileDependencies.includes(file) &&
230
+ // Server entrypoints must not contribute to the client stylesheet scan.
231
+ !(
232
+ environment.config.consumer === 'client' &&
233
+ /\.server\.[cm]?[jt]sx?$/.test(file)
234
+ ) &&
235
+ !Path.relative(sourceRoot(file)!, file)
192
236
  .split(Path.sep)
193
237
  .some((part) =>
194
238
  ['test', 'tests', '__tests__', 'fixtures', '__fixtures__'].includes(
@@ -323,7 +367,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
323
367
  }
324
368
 
325
369
  const pending = discoveries.get(environment)
326
- if (!pending || !eager(file)) return
370
+ if (!pending || !eager(file, environment)) return
327
371
 
328
372
  const sources = await pending
329
373
 
@@ -733,7 +777,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
733
777
  map: {
734
778
  ...output.map,
735
779
  sources: output.map.sources.map((source) =>
736
- source === null ? null : Path.join(root, source.slice(4)),
780
+ source === null ? null : (sourceFile(source) ?? source),
737
781
  ),
738
782
  },
739
783
  }
@@ -773,7 +817,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
773
817
  let line = 0
774
818
 
775
819
  for (const [id, output] of Object.entries(result.modules)) {
776
- if (!connected.has(Path.join(root, id.slice(4)))) continue
820
+ if (!connected.has(sourceFile(id)!)) continue
777
821
  if (!output.css) continue
778
822
 
779
823
  for (const mapping of Mapping.allMappings(
@@ -788,7 +832,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
788
832
  const location = {
789
833
  generated,
790
834
  original: mapping.original,
791
- source: Path.join(root, mapping.source.slice(4)),
835
+ source: sourceFile(mapping.source) ?? mapping.source,
792
836
  }
793
837
 
794
838
  if (mapping.name === undefined) Mapping.addMapping(map, location)
@@ -800,7 +844,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
800
844
  if (source !== null)
801
845
  Mapping.setSourceContent(
802
846
  map,
803
- Path.join(root, source.slice(4)),
847
+ sourceFile(source) ?? source,
804
848
  output.cssMap.sourcesContent?.[index] ?? null,
805
849
  )
806
850
 
@@ -808,7 +852,6 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
808
852
  line += output.css.split('\n').length
809
853
  }
810
854
 
811
- const appRoot = await Fs.realpath(root)
812
855
  const owners = new Map<string, string>()
813
856
 
814
857
  for (const id of new Set(Object.values(result.sharedAssetOwners ?? {}))) {
@@ -839,11 +882,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
839
882
  const raw = target.split(/[?#]/)[0]!
840
883
 
841
884
  const identity = result.sharedAssetOwners?.[key]
842
- const path = target.startsWith('app/')
843
- ? Path.join(root, decodeURIComponent(raw.slice(4)))
844
- : Path.isAbsolute(raw)
845
- ? decodeURIComponent(raw)
846
- : undefined
885
+ const path =
886
+ sourceFile(decodeURIComponent(raw)) ??
887
+ (Path.isAbsolute(raw) ? decodeURIComponent(raw) : undefined)
847
888
  if (!path) throw new Error('Asset path escapes the Vite graph.')
848
889
 
849
890
  const asset = await (async () => {
@@ -852,11 +893,12 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
852
893
  } catch (error) {
853
894
  if (
854
895
  (error as NodeJS.ErrnoException).code !== 'ENOENT' ||
855
- !identity?.startsWith('app/')
896
+ !identity ||
897
+ !sourceFile(identity)
856
898
  )
857
899
  throw error
858
900
 
859
- const importer = Path.join(root, identity.slice(4))
901
+ const importer = sourceFile(identity)!
860
902
  const specifier = Path.relative(Path.dirname(importer), path)
861
903
  .split(Path.sep)
862
904
  .join('/')
@@ -872,11 +914,12 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
872
914
  })()
873
915
  const file = asset.file
874
916
 
875
- const owner = identity?.startsWith('app/')
876
- ? appRoot
877
- : identity
878
- ? owners.get(identity)
879
- : undefined
917
+ const owner =
918
+ identity && sourceFile(identity)
919
+ ? sourceRoot(sourceFile(identity)!)
920
+ : identity
921
+ ? owners.get(identity)
922
+ : undefined
880
923
 
881
924
  const relative = owner ? Path.relative(owner, file) : '..'
882
925
  if (
@@ -961,7 +1004,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
961
1004
  ) as Mapping.EncodedSourceMap
962
1005
 
963
1006
  sharedMap.sources = sharedMap.sources.map((source) =>
964
- source?.startsWith('app/') ? Path.join(root, source.slice(4)) : source,
1007
+ source === null ? null : (sourceFile(source) ?? source),
965
1008
  )
966
1009
 
967
1010
  const output = result.modules[sourceId(entry.file)]!
@@ -982,7 +1025,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
982
1025
  map: {
983
1026
  ...output.map,
984
1027
  sources: output.map.sources.map((source) =>
985
- source === null ? null : Path.join(root, source.slice(4)),
1028
+ source === null ? null : (sourceFile(source) ?? source),
986
1029
  ),
987
1030
  },
988
1031
  }
@@ -1219,6 +1262,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1219
1262
  },
1220
1263
  },
1221
1264
  configureServer(server) {
1265
+ // Directory watches belong to the server, not module import dependencies.
1266
+ server.watcher.add(directories.slice(1))
1267
+
1222
1268
  server.middlewares.use((request, response, next) => {
1223
1269
  const path = new URL(request.url ?? '/', 'http://localhost').pathname
1224
1270
  const file = assets.get(path)
@@ -1236,8 +1282,21 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1236
1282
  }, next)
1237
1283
  })
1238
1284
  },
1239
- configResolved(config) {
1240
- root = config.root
1285
+ async configResolved(config) {
1286
+ root = await Fs.realpath(config.root)
1287
+ directories = [
1288
+ root,
1289
+ ...(await Promise.all(
1290
+ (options.include ?? []).map(async (directory) => {
1291
+ const file = await Fs.realpath(Path.resolve(root, directory))
1292
+ if (!(await Fs.stat(file)).isDirectory())
1293
+ throw new Error(
1294
+ `Zyzz include paths must be directories: ${directory}`,
1295
+ )
1296
+ return file
1297
+ }),
1298
+ )),
1299
+ ]
1241
1300
  if (native) return
1242
1301
  const lightning = (config.css.lightningcss ??= {})
1243
1302
  lightning.include =
@@ -1336,7 +1395,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1336
1395
 
1337
1396
  const state = entries(this.environment)
1338
1397
  const first = [...state.values()].find(
1339
- (entry) => entry.files.has(file) || eager(file),
1398
+ (entry) => entry.files.has(file) || eager(file, this.environment),
1340
1399
  )
1341
1400
  if (!first && !removed.get(this.environment)?.has(file)) return modules
1342
1401
 
@@ -1374,7 +1433,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1374
1433
  if (type === 'delete') ids.add(cssId(file))
1375
1434
 
1376
1435
  for (const entry of entries(this.environment).values()) {
1377
- if (!entry.files.has(file) && !eager(file)) continue
1436
+ if (!entry.files.has(file) && !eager(file, this.environment)) continue
1378
1437
 
1379
1438
  // Theme scopes affect CSS even when Vite's JavaScript import was erased.
1380
1439
  if (
@@ -1635,6 +1694,8 @@ export declare namespace zyzz {
1635
1694
  type Options = {
1636
1695
  /** False retains authored calls and requires explicit identities where needed. */
1637
1696
  readonly compiler?: boolean | undefined
1697
+ /** Additional source directories, absolute or relative to the Vite root. */
1698
+ readonly include?: readonly string[] | undefined
1638
1699
  /** Native context captured at creation. Disables CSS delivery and initialization scripts. */
1639
1700
  readonly native?: Graph.compile.Options['native']
1640
1701
  /** Include the CSS reset. Defaults to false. */
@@ -220,6 +220,10 @@ export function create() {
220
220
  owner?: Token.Contract,
221
221
  ): string {
222
222
  if (Token.is(value)) return serialize(value)
223
+ if (Token.isExpression(value))
224
+ return value.parts
225
+ .map((part) => (typeof part === 'string' ? part : serialize(part)))
226
+ .join('')
223
227
  if (typeof value !== 'object') return String(value)
224
228
  if ('default' in value) {
225
229
  // Separate fallback properties preserve extensions and resolve references within each scope.