zyzz 0.0.2 → 0.0.5
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/README.md +34 -19
- package/dist/Config.d.ts +16 -167
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +7 -246
- package/dist/Config.js.map +1 -1
- package/dist/Style.d.ts +19 -13
- package/dist/Style.d.ts.map +1 -1
- package/dist/Style.js +73 -7
- package/dist/Style.js.map +1 -1
- package/dist/Vars.d.ts +114 -0
- package/dist/Vars.d.ts.map +1 -0
- package/dist/Vars.js +42 -0
- package/dist/Vars.js.map +1 -0
- package/dist/compiler/Graph.js +11 -8
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Native.d.ts +5 -5
- package/dist/compiler/Native.d.ts.map +1 -1
- package/dist/compiler/Native.js +21 -21
- package/dist/compiler/Native.js.map +1 -1
- package/dist/compiler/Source.d.ts +10 -6
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +66 -11
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts +1 -1
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +59 -21
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/Compositions.d.ts +5 -0
- package/dist/compiler/internal/Compositions.d.ts.map +1 -1
- package/dist/compiler/internal/Compositions.js +22 -3
- package/dist/compiler/internal/Compositions.js.map +1 -1
- package/dist/compiler/internal/Configurations.d.ts.map +1 -1
- package/dist/compiler/internal/Configurations.js +73 -12
- package/dist/compiler/internal/Configurations.js.map +1 -1
- package/dist/compiler/internal/Contract.d.ts +1 -1
- package/dist/compiler/internal/Contract.d.ts.map +1 -1
- package/dist/compiler/internal/Contract.js +162 -13
- package/dist/compiler/internal/Contract.js.map +1 -1
- package/dist/compiler/internal/Contributions.js +1 -1
- package/dist/compiler/internal/Contributions.js.map +1 -1
- package/dist/compiler/internal/Dynamic.js +1 -1
- package/dist/compiler/internal/Dynamic.js.map +1 -1
- package/dist/compiler/internal/Expression.d.ts +3 -0
- package/dist/compiler/internal/Expression.d.ts.map +1 -1
- package/dist/compiler/internal/Expression.js +21 -0
- package/dist/compiler/internal/Expression.js.map +1 -1
- package/dist/compiler/internal/Identifiers.d.ts +1 -1
- package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
- package/dist/compiler/internal/Identifiers.js +2 -2
- package/dist/compiler/internal/Identifiers.js.map +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +3 -3
- package/dist/compiler/internal/NativeBindings.js.map +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
- package/dist/compiler/internal/PackedStyles.js +15 -25
- package/dist/compiler/internal/PackedStyles.js.map +1 -1
- package/dist/compiler/internal/Recipes.js +1 -1
- package/dist/compiler/internal/Recipes.js.map +1 -1
- package/dist/compiler/internal/Selectors.js +1 -1
- package/dist/compiler/internal/Selectors.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts +12 -3
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +192 -52
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
- package/dist/compiler/internal/Vars.d.ts.map +1 -0
- package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
- package/dist/compiler/internal/Vars.js.map +1 -0
- package/dist/default.d.ts +451 -1649
- package/dist/default.d.ts.map +1 -1
- package/dist/default.js +389 -80
- package/dist/default.js.map +2 -2
- package/dist/default.js.zyzz.json +1 -1
- package/dist/esbuild/index.d.ts +3 -0
- package/dist/esbuild/index.d.ts.map +1 -0
- package/dist/esbuild/index.js +5 -0
- package/dist/esbuild/index.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/Authoring.d.ts +1 -1
- package/dist/internal/Authoring.d.ts.map +1 -1
- package/dist/internal/Authoring.js +4 -4
- package/dist/internal/Authoring.js.map +1 -1
- package/dist/internal/Binding.d.ts +1 -1
- package/dist/internal/Binding.d.ts.map +1 -1
- package/dist/internal/Completion.d.ts +19 -0
- package/dist/internal/Completion.d.ts.map +1 -0
- package/dist/internal/Completion.js +2 -0
- package/dist/internal/Completion.js.map +1 -0
- package/dist/internal/Condition.d.ts +3 -1
- package/dist/internal/Condition.d.ts.map +1 -1
- package/dist/internal/Condition.js.map +1 -1
- package/dist/internal/Configuration.d.ts +248 -0
- package/dist/internal/Configuration.d.ts.map +1 -0
- package/dist/internal/Configuration.js +343 -0
- package/dist/internal/Configuration.js.map +1 -0
- package/dist/internal/Shorthands.d.ts +1 -1
- package/dist/internal/Shorthands.d.ts.map +1 -1
- package/dist/internal/Shorthands.js +1 -0
- package/dist/internal/Shorthands.js.map +1 -1
- package/dist/{Theme.d.ts → internal/Theme.d.ts} +24 -19
- package/dist/internal/Theme.d.ts.map +1 -0
- package/dist/{Theme.js → internal/Theme.js} +57 -14
- package/dist/internal/Theme.js.map +1 -0
- package/dist/internal/Token.d.ts +46 -16
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js +89 -5
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Typography.d.ts +31 -0
- package/dist/internal/Typography.d.ts.map +1 -0
- package/dist/internal/Typography.js +54 -0
- package/dist/internal/Typography.js.map +1 -0
- package/dist/internal/Value.d.ts +6 -1
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VariableSets.d.ts +20 -0
- package/dist/internal/VariableSets.d.ts.map +1 -0
- package/dist/internal/VariableSets.js +317 -0
- package/dist/internal/VariableSets.js.map +1 -0
- package/dist/next/loader.js +15 -5
- package/dist/next/loader.js.map +1 -1
- package/dist/node/Host.d.ts.map +1 -1
- package/dist/node/Host.js +1 -1
- package/dist/node/Host.js.map +1 -1
- package/dist/react-native/Host.d.ts +2 -2
- package/dist/react-native/Host.d.ts.map +1 -1
- package/dist/react-native/Host.js +4 -4
- package/dist/react-native/Host.js.map +1 -1
- package/dist/react-native/StyleSheet.d.ts +9 -8
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +29 -23
- package/dist/react-native/StyleSheet.js.map +1 -1
- package/dist/react-native/Variants.d.ts +3 -3
- package/dist/react-native/Variants.d.ts.map +1 -1
- package/dist/react-native/Variants.js +2 -2
- package/dist/react-native/react.d.ts +1 -1
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +5 -5
- package/dist/react-native/react.js.map +1 -1
- package/dist/rollup/index.d.ts +3 -0
- package/dist/rollup/index.d.ts.map +1 -0
- package/dist/rollup/index.js +5 -0
- package/dist/rollup/index.js.map +1 -0
- package/dist/runtime/Appearance.d.ts +7 -7
- package/dist/runtime/Appearance.d.ts.map +1 -1
- package/dist/runtime/Appearance.js +15 -15
- package/dist/runtime/Appearance.js.map +1 -1
- package/dist/runtime/Dynamic.d.ts.map +1 -1
- package/dist/runtime/Dynamic.js +4 -1
- package/dist/runtime/Dynamic.js.map +1 -1
- package/dist/runtime/NativeContext.d.ts +4 -4
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +5 -5
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/runtime/NativeDynamic.d.ts +1 -1
- package/dist/runtime/NativeDynamic.d.ts.map +1 -1
- package/dist/runtime/NativeDynamic.js.map +1 -1
- package/dist/runtime/Props.d.ts +1 -1
- package/dist/runtime/Props.js +3 -3
- package/dist/runtime/Props.js.map +1 -1
- package/dist/runtime/Selection.d.ts +14 -10
- package/dist/runtime/Selection.d.ts.map +1 -1
- package/dist/runtime/Selection.js +19 -18
- package/dist/runtime/Selection.js.map +1 -1
- package/dist/styleFunction.d.ts +14 -14
- package/dist/styleFunction.d.ts.map +1 -1
- package/dist/styleFunction.js.map +1 -1
- package/dist/unplugin/index.d.ts +20 -0
- package/dist/unplugin/index.d.ts.map +1 -0
- package/dist/unplugin/index.js +498 -0
- package/dist/unplugin/index.js.map +1 -0
- package/dist/variants.d.ts +3 -3
- package/dist/variants.d.ts.map +1 -1
- package/dist/variants.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +6 -3
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts +4 -3
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +4 -4
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +3 -2
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +42 -6
- package/dist/web/internal/Themes.js.map +1 -1
- package/dist/webpack/index.d.ts +3 -0
- package/dist/webpack/index.d.ts.map +1 -0
- package/dist/webpack/index.js +5 -0
- package/dist/webpack/index.js.map +1 -0
- package/package.json +23 -2
- package/src/Config.ts +43 -684
- package/src/Style.ts +167 -40
- package/src/Vars.ts +299 -0
- package/src/compiler/Graph.ts +14 -9
- package/src/compiler/Native.ts +29 -32
- package/src/compiler/Source.ts +85 -18
- package/src/compiler/Transform.ts +78 -22
- package/src/compiler/internal/Compositions.ts +36 -3
- package/src/compiler/internal/Configurations.ts +83 -11
- package/src/compiler/internal/Contract.ts +223 -17
- package/src/compiler/internal/Contributions.ts +1 -1
- package/src/compiler/internal/Dynamic.ts +1 -1
- package/src/compiler/internal/Expression.ts +26 -0
- package/src/compiler/internal/Identifiers.ts +2 -1
- package/src/compiler/internal/NativeBindings.ts +3 -3
- package/src/compiler/internal/PackedStyles.ts +22 -26
- package/src/compiler/internal/Recipes.ts +1 -1
- package/src/compiler/internal/Selectors.ts +1 -1
- package/src/compiler/internal/Themes.ts +266 -57
- package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
- package/src/default.ts +868 -156
- package/src/esbuild/index.ts +5 -0
- package/src/index.ts +4 -4
- package/src/internal/Authoring.ts +5 -7
- package/src/internal/Binding.ts +1 -1
- package/src/internal/Completion.ts +42 -0
- package/src/internal/Condition.ts +31 -1
- package/src/internal/Configuration.ts +970 -0
- package/src/internal/Shorthands.ts +2 -0
- package/src/{Theme.ts → internal/Theme.ts} +174 -48
- package/src/internal/Token.ts +258 -85
- package/src/internal/Typography.ts +106 -0
- package/src/internal/Value.ts +45 -26
- package/src/internal/VariableSets.ts +458 -0
- package/src/next/loader.ts +19 -5
- package/src/node/Host.ts +1 -2
- package/src/react-native/Host.ts +6 -6
- package/src/react-native/StyleSheet.ts +49 -42
- package/src/react-native/Variants.ts +3 -3
- package/src/react-native/react.ts +6 -6
- package/src/rollup/index.ts +5 -0
- package/src/runtime/Appearance.ts +20 -20
- package/src/runtime/Dynamic.ts +4 -1
- package/src/runtime/NativeContext.ts +8 -8
- package/src/runtime/NativeDynamic.ts +2 -4
- package/src/runtime/Props.ts +3 -3
- package/src/runtime/Selection.ts +44 -33
- package/src/styleFunction.ts +22 -19
- package/src/unplugin/index.ts +600 -0
- package/src/variants.ts +7 -5
- package/src/vite/index.ts +5 -4
- package/src/web/Css.ts +10 -7
- package/src/web/internal/Themes.ts +52 -9
- package/src/webpack/index.ts +5 -0
- package/dist/Theme.d.ts.map +0 -1
- package/dist/Theme.js.map +0 -1
- package/dist/compiler/internal/Variables.d.ts.map +0 -1
- package/dist/compiler/internal/Variables.js.map +0 -1
package/src/styleFunction.ts
CHANGED
|
@@ -4,25 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as Authoring from './internal/Authoring.js'
|
|
6
6
|
import type * as Binding from './internal/Binding.js'
|
|
7
|
+
import type * as Completion from './internal/Completion.js'
|
|
7
8
|
import type * as Literal from './internal/Literal.js'
|
|
8
9
|
import type * as Style from './Style.js'
|
|
9
10
|
|
|
10
|
-
/** Restricts callback hints to inferred declarations instead of expanding every property grammar. */
|
|
11
|
-
type CompletionProperties<styles = Record<string, unknown>> = {
|
|
12
|
-
readonly [property in keyof typeof Literal.rules & keyof styles]?:
|
|
13
|
-
| Literal.Properties[property]
|
|
14
|
-
| (string & {})
|
|
15
|
-
| number
|
|
16
|
-
| object
|
|
17
|
-
}
|
|
18
|
-
|
|
19
11
|
type Keys<value> = value extends unknown ? keyof value : never
|
|
20
12
|
|
|
13
|
+
// Keep named and custom-property instantiations separate so framework interfaces need no index signature.
|
|
14
|
+
type InlineProperties<properties extends string> = {
|
|
15
|
+
readonly [property in properties]?: string | number | undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
21
18
|
type VariableOptions<input> = input extends {
|
|
22
|
-
readonly
|
|
19
|
+
readonly vars: infer variables
|
|
23
20
|
}
|
|
24
21
|
? {
|
|
25
|
-
readonly
|
|
22
|
+
readonly vars: {
|
|
26
23
|
readonly [key in keyof variables]: string extends key
|
|
27
24
|
? variables[key]
|
|
28
25
|
: key extends `--${string}`
|
|
@@ -49,7 +46,7 @@ export function style<
|
|
|
49
46
|
values: values,
|
|
50
47
|
) => styles &
|
|
51
48
|
NoInfer<Style.Accepted<styles, {}, true> & Binding.Checked<styles>>) &
|
|
52
|
-
|
|
49
|
+
Completion.Properties<{}, NoInfer<styles>>,
|
|
53
50
|
// Exclude invalid callbacks by arity so they cannot mask object-property diagnostics.
|
|
54
51
|
...options: Parameters<callback> extends [Record<string, string | number>]
|
|
55
52
|
? values extends Binding.Inputs<values>
|
|
@@ -60,7 +57,7 @@ export function style<
|
|
|
60
57
|
export function style<const styles extends Record<string, unknown>>(
|
|
61
58
|
styles: styles &
|
|
62
59
|
NoInfer<Style.Accepted<styles, {}, true>> &
|
|
63
|
-
|
|
60
|
+
Completion.Properties,
|
|
64
61
|
options?: style.DefinitionOptions,
|
|
65
62
|
): style.ReturnType
|
|
66
63
|
export function style(
|
|
@@ -80,7 +77,7 @@ export declare namespace style {
|
|
|
80
77
|
type DefinitionOptions = { readonly id?: string | undefined }
|
|
81
78
|
/** Callable compiled bindings with required scalar inputs and styling overrides. */
|
|
82
79
|
type Dynamic<values, output extends Output = 'react'> = Reference &
|
|
83
|
-
(<const input extends values & Options
|
|
80
|
+
(<const input extends values & Options<output>>(
|
|
84
81
|
input: input &
|
|
85
82
|
VariableOptions<input> &
|
|
86
83
|
Record<Exclude<keyof input, keyof values | keyof Options>, never>,
|
|
@@ -90,13 +87,19 @@ export declare namespace style {
|
|
|
90
87
|
type ErrorType = Error
|
|
91
88
|
|
|
92
89
|
/** Styling overrides consumed by a transformed definition. */
|
|
93
|
-
type Options = {
|
|
90
|
+
type Options<output extends Output = 'react'> = {
|
|
94
91
|
/** External class names appended to the generated classes. */
|
|
95
92
|
readonly className?: string | undefined
|
|
96
|
-
/**
|
|
97
|
-
readonly style?:
|
|
93
|
+
/** Inline overrides. React accepts scalar values, while HTML requires CSS units. */
|
|
94
|
+
readonly style?:
|
|
95
|
+
| (output extends 'html'
|
|
96
|
+
? Literal.Properties
|
|
97
|
+
:
|
|
98
|
+
| InlineProperties<keyof typeof Literal.rules>
|
|
99
|
+
| InlineProperties<keyof Literal.Properties>)
|
|
100
|
+
| undefined
|
|
98
101
|
/** Inline custom-property assignments, merged before explicit style overrides. */
|
|
99
|
-
readonly
|
|
102
|
+
readonly vars?:
|
|
100
103
|
| Readonly<Record<`--${string}`, string | number | undefined>>
|
|
101
104
|
| undefined
|
|
102
105
|
}
|
|
@@ -118,7 +121,7 @@ export declare namespace style {
|
|
|
118
121
|
|
|
119
122
|
/** Callable definition; source rewriting supplies its implementation. */
|
|
120
123
|
type ReturnType<output extends Output = 'react'> = Reference &
|
|
121
|
-
(<const options extends Options = Options
|
|
124
|
+
(<const options extends Options<output> = Options<output>>(
|
|
122
125
|
options?: options &
|
|
123
126
|
VariableOptions<options> &
|
|
124
127
|
Record<Exclude<Keys<options>, keyof Options>, never>,
|
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
/** Connects the static compiler to portable bundler hooks and the Vite adapter. @module */
|
|
2
|
+
import * as Mapping from '@jridgewell/gen-mapping'
|
|
3
|
+
import * as Crypto from 'node:crypto'
|
|
4
|
+
import * as Fs from 'node:fs/promises'
|
|
5
|
+
import * as Path from 'node:path'
|
|
6
|
+
import * as Parser from 'oxc-parser'
|
|
7
|
+
import { createUnplugin } from 'unplugin'
|
|
8
|
+
import type { UnpluginBuildContext } from 'unplugin'
|
|
9
|
+
import * as Graph from '../compiler/Graph.js'
|
|
10
|
+
import * as AtRules from '../compiler/internal/AtRules.js'
|
|
11
|
+
import * as Catalogs from '../compiler/internal/Catalogs.js'
|
|
12
|
+
import * as Contract from '../compiler/internal/Contract.js'
|
|
13
|
+
import * as Vite from '../vite/index.js'
|
|
14
|
+
|
|
15
|
+
/** Portable build configuration. Vite uses the options from `zyzz/vite` instead. */
|
|
16
|
+
export type Options = {
|
|
17
|
+
/** Rewrite authoring calls. Defaults to true. */
|
|
18
|
+
readonly compiler?: boolean | undefined
|
|
19
|
+
/** Source directory scanned for modules and global contributions. Defaults to the working directory. */
|
|
20
|
+
readonly root?: string | undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const excludedPattern = /\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/
|
|
24
|
+
const sourcePattern = /\.[cm]?[jt]sx?$/
|
|
25
|
+
|
|
26
|
+
const portable = createUnplugin<Options | undefined, false>(
|
|
27
|
+
(options = {}, meta) => {
|
|
28
|
+
const root = Path.resolve(options.root ?? process.cwd())
|
|
29
|
+
const compiler = Graph.create()
|
|
30
|
+
let graph: Graph.compile.ReturnType | undefined
|
|
31
|
+
let pending: Promise<void> | undefined
|
|
32
|
+
let files = new Set<string>()
|
|
33
|
+
let directories = new Set<string>()
|
|
34
|
+
let sources = new Map<string, string>()
|
|
35
|
+
let assets = new Map<string, Uint8Array>()
|
|
36
|
+
let css = ''
|
|
37
|
+
let cssMap = ''
|
|
38
|
+
let script = ''
|
|
39
|
+
|
|
40
|
+
function identity(file: string) {
|
|
41
|
+
return `app/${Path.relative(root, file).split(Path.sep).join('/')}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function emit(context: UnpluginBuildContext) {
|
|
45
|
+
if (!graph) return
|
|
46
|
+
for (const [fileName, source] of assets)
|
|
47
|
+
context.emitFile({
|
|
48
|
+
type: 'asset',
|
|
49
|
+
fileName,
|
|
50
|
+
source: Uint8Array.from(source),
|
|
51
|
+
})
|
|
52
|
+
context.emitFile({
|
|
53
|
+
type: 'asset',
|
|
54
|
+
fileName: 'zyzz.css',
|
|
55
|
+
source: `${css}/*# sourceMappingURL=zyzz.css.map */\n`,
|
|
56
|
+
})
|
|
57
|
+
context.emitFile({
|
|
58
|
+
type: 'asset',
|
|
59
|
+
fileName: 'zyzz.css.map',
|
|
60
|
+
source: cssMap,
|
|
61
|
+
})
|
|
62
|
+
context.emitFile({
|
|
63
|
+
type: 'asset',
|
|
64
|
+
fileName: 'zyzz.js',
|
|
65
|
+
source: script || '/* No saved theme selections. */\n',
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function transform(code: string, id: string) {
|
|
70
|
+
const output = graph?.modules[identity(id)]
|
|
71
|
+
if (!output) return
|
|
72
|
+
if (code !== sources.get(id))
|
|
73
|
+
throw new Error(`Zyzz must run before source transforms: ${id}`)
|
|
74
|
+
return {
|
|
75
|
+
code: output.code,
|
|
76
|
+
map: JSON.stringify({
|
|
77
|
+
...output.map,
|
|
78
|
+
sources: output.map.sources.map((source) =>
|
|
79
|
+
source?.startsWith('app/')
|
|
80
|
+
? Path.join(root, source.slice(4))
|
|
81
|
+
: source,
|
|
82
|
+
),
|
|
83
|
+
}),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
buildEnd:
|
|
89
|
+
meta.framework === 'webpack'
|
|
90
|
+
? undefined
|
|
91
|
+
: function () {
|
|
92
|
+
emit(this)
|
|
93
|
+
},
|
|
94
|
+
async buildStart() {
|
|
95
|
+
pending = (async () => {
|
|
96
|
+
graph = undefined
|
|
97
|
+
files = new Set()
|
|
98
|
+
directories = new Set()
|
|
99
|
+
sources = new Map()
|
|
100
|
+
assets = new Map()
|
|
101
|
+
const modules: Record<string, string> = Object.create(null)
|
|
102
|
+
const imports: Record<
|
|
103
|
+
string,
|
|
104
|
+
Record<string, string | null>
|
|
105
|
+
> = Object.create(null)
|
|
106
|
+
const contracts: Record<string, string> = Object.create(null)
|
|
107
|
+
|
|
108
|
+
async function scan(directory: string): Promise<void> {
|
|
109
|
+
directories.add(directory)
|
|
110
|
+
const entries = await Fs.readdir(directory, { withFileTypes: true })
|
|
111
|
+
|
|
112
|
+
for (const entry of entries.sort((a, b) =>
|
|
113
|
+
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
|
|
114
|
+
)) {
|
|
115
|
+
if (
|
|
116
|
+
entry.name.startsWith('.') ||
|
|
117
|
+
[
|
|
118
|
+
'node_modules',
|
|
119
|
+
'dist',
|
|
120
|
+
'build',
|
|
121
|
+
'coverage',
|
|
122
|
+
'test',
|
|
123
|
+
'tests',
|
|
124
|
+
'__tests__',
|
|
125
|
+
'fixtures',
|
|
126
|
+
'__fixtures__',
|
|
127
|
+
].includes(entry.name)
|
|
128
|
+
)
|
|
129
|
+
continue
|
|
130
|
+
const file = Path.join(directory, entry.name)
|
|
131
|
+
|
|
132
|
+
if (entry.isDirectory()) await scan(file)
|
|
133
|
+
else if (
|
|
134
|
+
entry.isFile() &&
|
|
135
|
+
sourcePattern.test(file) &&
|
|
136
|
+
!excludedPattern.test(file)
|
|
137
|
+
) {
|
|
138
|
+
const source = await Fs.readFile(file, 'utf8')
|
|
139
|
+
|
|
140
|
+
files.add(file)
|
|
141
|
+
sources.set(file, source)
|
|
142
|
+
modules[identity(file)] = source
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const resolve = async (specifier: string, importer: string) => {
|
|
148
|
+
if ('resolve' in this && typeof this.resolve === 'function') {
|
|
149
|
+
const result = await (
|
|
150
|
+
this as UnpluginBuildContext & {
|
|
151
|
+
resolve: (
|
|
152
|
+
id: string,
|
|
153
|
+
importer: string,
|
|
154
|
+
) => Promise<{
|
|
155
|
+
id: string
|
|
156
|
+
external?: boolean | 'absolute' | 'relative'
|
|
157
|
+
} | null>
|
|
158
|
+
}
|
|
159
|
+
).resolve(specifier, importer)
|
|
160
|
+
return result && !result.external ? result.id : undefined
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const native = this.getNativeBuildContext?.()
|
|
164
|
+
if (native?.framework === 'esbuild') {
|
|
165
|
+
const result = await native.build.resolve(specifier, {
|
|
166
|
+
importer,
|
|
167
|
+
kind: 'import-statement',
|
|
168
|
+
resolveDir: Path.dirname(importer),
|
|
169
|
+
})
|
|
170
|
+
if (result.errors.length)
|
|
171
|
+
throw new Error(
|
|
172
|
+
result.errors.map((error) => error.text).join('\n'),
|
|
173
|
+
)
|
|
174
|
+
return result.external ? undefined : result.path
|
|
175
|
+
}
|
|
176
|
+
if (native?.framework === 'webpack') {
|
|
177
|
+
const resolver = native.compiler.resolverFactory.get('normal', {
|
|
178
|
+
dependencyType: 'esm',
|
|
179
|
+
})
|
|
180
|
+
return new Promise<string | undefined>((accept, reject) => {
|
|
181
|
+
resolver.resolve(
|
|
182
|
+
{},
|
|
183
|
+
Path.dirname(importer),
|
|
184
|
+
specifier,
|
|
185
|
+
{},
|
|
186
|
+
(error, result) => {
|
|
187
|
+
if (error) reject(error)
|
|
188
|
+
else accept(result || undefined)
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
throw new Error('The bundler does not expose module resolution.')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function contract(
|
|
197
|
+
file: string,
|
|
198
|
+
required = false,
|
|
199
|
+
): Promise<boolean> {
|
|
200
|
+
if (Object.hasOwn(contracts, file)) return true
|
|
201
|
+
const sidecar = `${file}.zyzz.json`
|
|
202
|
+
|
|
203
|
+
files.add(sidecar)
|
|
204
|
+
try {
|
|
205
|
+
contracts[file] = await Fs.readFile(sidecar, 'utf8')
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (
|
|
208
|
+
!required &&
|
|
209
|
+
(error as NodeJS.ErrnoException).code === 'ENOENT'
|
|
210
|
+
)
|
|
211
|
+
return false
|
|
212
|
+
|
|
213
|
+
throw error
|
|
214
|
+
}
|
|
215
|
+
const metadata = Contract.read(contracts[file]!, new Map(), file)
|
|
216
|
+
for (const section of metadata.stylesheets) {
|
|
217
|
+
let owner = file
|
|
218
|
+
for (const specifier of section.dependency ?? []) {
|
|
219
|
+
const target = await resolve(specifier, owner)
|
|
220
|
+
if (!target)
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Unable to resolve packed stylesheet dependency: ${specifier}`,
|
|
223
|
+
)
|
|
224
|
+
;(imports[owner] ??= Object.create(null))[specifier] = target
|
|
225
|
+
await contract(target, true)
|
|
226
|
+
owner = target
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return true
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
await scan(root)
|
|
233
|
+
for (const [file, source] of sources) {
|
|
234
|
+
const links = (imports[identity(file)] = Object.create(null))
|
|
235
|
+
for (const node of Parser.parseSync(file, source, {
|
|
236
|
+
sourceType: 'module',
|
|
237
|
+
}).program.body) {
|
|
238
|
+
if (
|
|
239
|
+
(node.type !== 'ImportDeclaration' &&
|
|
240
|
+
node.type !== 'ExportNamedDeclaration' &&
|
|
241
|
+
node.type !== 'ExportAllDeclaration') ||
|
|
242
|
+
!node.source
|
|
243
|
+
)
|
|
244
|
+
continue
|
|
245
|
+
if (
|
|
246
|
+
node.type === 'ImportDeclaration'
|
|
247
|
+
? node.importKind === 'type'
|
|
248
|
+
: node.exportKind === 'type'
|
|
249
|
+
)
|
|
250
|
+
continue
|
|
251
|
+
if (
|
|
252
|
+
'specifiers' in node &&
|
|
253
|
+
node.specifiers.length &&
|
|
254
|
+
node.specifiers.every((specifier) =>
|
|
255
|
+
specifier.type === 'ImportSpecifier'
|
|
256
|
+
? specifier.importKind === 'type'
|
|
257
|
+
: specifier.type === 'ExportSpecifier' &&
|
|
258
|
+
specifier.exportKind === 'type',
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
const specifier = node.source.value
|
|
264
|
+
links[specifier] = null
|
|
265
|
+
if (
|
|
266
|
+
specifier === 'zyzz' ||
|
|
267
|
+
(specifier.startsWith('zyzz/') &&
|
|
268
|
+
specifier !== 'zyzz/default') ||
|
|
269
|
+
/^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(specifier) ||
|
|
270
|
+
specifier.includes('?')
|
|
271
|
+
)
|
|
272
|
+
continue
|
|
273
|
+
const target = await resolve(specifier, file)
|
|
274
|
+
if (!target || !sourcePattern.test(target)) continue
|
|
275
|
+
files.add(target)
|
|
276
|
+
if (sources.has(target)) links[specifier] = identity(target)
|
|
277
|
+
else if (await contract(target)) links[specifier] = target
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const result = compiler.compile({
|
|
282
|
+
compiler: options.compiler,
|
|
283
|
+
contracts,
|
|
284
|
+
imports,
|
|
285
|
+
modules,
|
|
286
|
+
})
|
|
287
|
+
const copied = new Map<string, string>()
|
|
288
|
+
const owners = new Map<string, string>()
|
|
289
|
+
|
|
290
|
+
async function owner(id: string): Promise<string> {
|
|
291
|
+
if (id.startsWith('app/')) return Fs.realpath(root)
|
|
292
|
+
const cached = owners.get(id)
|
|
293
|
+
if (cached) return cached
|
|
294
|
+
|
|
295
|
+
let directory = Path.dirname(id)
|
|
296
|
+
for (;;) {
|
|
297
|
+
try {
|
|
298
|
+
await Fs.access(Path.join(directory, 'package.json'))
|
|
299
|
+
const real = await Fs.realpath(directory)
|
|
300
|
+
owners.set(id, real)
|
|
301
|
+
return real
|
|
302
|
+
} catch (error) {
|
|
303
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT')
|
|
304
|
+
throw error
|
|
305
|
+
const parent = Path.dirname(directory)
|
|
306
|
+
if (parent === directory)
|
|
307
|
+
throw new Error(
|
|
308
|
+
'Packed assets require an owning package.json.',
|
|
309
|
+
)
|
|
310
|
+
directory = parent
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function copy(file: string, boundary: string): Promise<string> {
|
|
316
|
+
const real = await Fs.realpath(file)
|
|
317
|
+
if (!inside(boundary, real))
|
|
318
|
+
throw new Error('Asset path escapes its owning package.')
|
|
319
|
+
files.add(real)
|
|
320
|
+
const cached = copied.get(real)
|
|
321
|
+
if (cached) return cached
|
|
322
|
+
|
|
323
|
+
const content = await Fs.readFile(real)
|
|
324
|
+
const name = `zyzz-assets/${Crypto.hash('sha256', real + '\0' + content.toString('base64')).slice(0, 16)}${Path.extname(real)}`
|
|
325
|
+
copied.set(real, name)
|
|
326
|
+
if (real.endsWith('.css')) {
|
|
327
|
+
const processed = await stylesheet(
|
|
328
|
+
content.toString(),
|
|
329
|
+
undefined,
|
|
330
|
+
real,
|
|
331
|
+
boundary,
|
|
332
|
+
Path.posix.dirname(name),
|
|
333
|
+
)
|
|
334
|
+
assets.set(name, processed.code)
|
|
335
|
+
} else assets.set(name, content)
|
|
336
|
+
return name
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function stylesheet(
|
|
340
|
+
source: string,
|
|
341
|
+
map: string | undefined,
|
|
342
|
+
file: string,
|
|
343
|
+
boundary: string,
|
|
344
|
+
outputDirectory = '.',
|
|
345
|
+
targets?: Readonly<Record<string, string>>,
|
|
346
|
+
) {
|
|
347
|
+
const urls = new Set<string>()
|
|
348
|
+
AtRules.transform({
|
|
349
|
+
code: Buffer.from(source),
|
|
350
|
+
filename: file,
|
|
351
|
+
visitor: {
|
|
352
|
+
Rule(rule) {
|
|
353
|
+
if (rule.type === 'import') urls.add(rule.value.url)
|
|
354
|
+
},
|
|
355
|
+
Url(url) {
|
|
356
|
+
urls.add(url.url)
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
})
|
|
360
|
+
const replacements = new Map<string, string>()
|
|
361
|
+
for (const url of urls) {
|
|
362
|
+
const target = targets?.[url]
|
|
363
|
+
if (
|
|
364
|
+
!target &&
|
|
365
|
+
(!url || /^(?:[a-z][a-z\d+.-]*:|[/?#])/i.test(url))
|
|
366
|
+
)
|
|
367
|
+
continue
|
|
368
|
+
const raw = (target ?? url).split(/[?#]/)[0]!
|
|
369
|
+
const path = (() => {
|
|
370
|
+
if (!target)
|
|
371
|
+
return Path.resolve(
|
|
372
|
+
Path.dirname(file),
|
|
373
|
+
decodeURIComponent(raw),
|
|
374
|
+
)
|
|
375
|
+
if (raw.startsWith('app/'))
|
|
376
|
+
return Path.join(root, decodeURIComponent(raw.slice(4)))
|
|
377
|
+
return decodeURIComponent(raw)
|
|
378
|
+
})()
|
|
379
|
+
const assetOwner = target
|
|
380
|
+
? await owner(result.sharedAssetOwners?.[url] ?? 'app/')
|
|
381
|
+
: boundary
|
|
382
|
+
const name = await copy(path, assetOwner)
|
|
383
|
+
replacements.set(
|
|
384
|
+
url,
|
|
385
|
+
Path.posix.relative(outputDirectory, name) +
|
|
386
|
+
(target ?? url).slice(raw.length),
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
return AtRules.transform({
|
|
390
|
+
code: Buffer.from(source),
|
|
391
|
+
filename: file,
|
|
392
|
+
...(map === undefined
|
|
393
|
+
? {}
|
|
394
|
+
: { inputSourceMap: map, sourceMap: true }),
|
|
395
|
+
visitor: {
|
|
396
|
+
Rule(rule) {
|
|
397
|
+
if (rule.type !== 'import') return
|
|
398
|
+
const url = replacements.get(rule.value.url)
|
|
399
|
+
if (url) return AtRules.relocateImport(rule.value, url)
|
|
400
|
+
},
|
|
401
|
+
Url(url) {
|
|
402
|
+
const target = replacements.get(url.url)
|
|
403
|
+
if (target) return { ...url, url: target }
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
})
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const shared = await stylesheet(
|
|
410
|
+
result.sharedCss ?? '',
|
|
411
|
+
JSON.stringify(
|
|
412
|
+
result.sharedCssMap ??
|
|
413
|
+
Mapping.toEncodedMap(new Mapping.GenMapping()),
|
|
414
|
+
),
|
|
415
|
+
Path.join(root, 'zyzz.css'),
|
|
416
|
+
await Fs.realpath(root),
|
|
417
|
+
'.',
|
|
418
|
+
result.sharedAssets,
|
|
419
|
+
)
|
|
420
|
+
const chunks = [
|
|
421
|
+
{
|
|
422
|
+
code: Buffer.from(shared.code).toString(),
|
|
423
|
+
map: Buffer.from(shared.map!).toString(),
|
|
424
|
+
},
|
|
425
|
+
]
|
|
426
|
+
const seen = new Set<string>()
|
|
427
|
+
async function visit(id: string): Promise<void> {
|
|
428
|
+
if (seen.has(id)) return
|
|
429
|
+
seen.add(id)
|
|
430
|
+
for (const dependency of result.dependencies[id] ?? [])
|
|
431
|
+
await visit(dependency)
|
|
432
|
+
const output = result.modules[id]
|
|
433
|
+
if (output) {
|
|
434
|
+
const processed = await stylesheet(
|
|
435
|
+
output.css,
|
|
436
|
+
JSON.stringify(output.cssMap),
|
|
437
|
+
Path.join(root, id.slice(4)),
|
|
438
|
+
await Fs.realpath(root),
|
|
439
|
+
)
|
|
440
|
+
chunks.push({
|
|
441
|
+
code: Buffer.from(processed.code).toString(),
|
|
442
|
+
map: Buffer.from(processed.map!).toString(),
|
|
443
|
+
})
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const ids = Object.keys(result.modules)
|
|
447
|
+
const imported = new Set(Object.values(result.dependencies).flat())
|
|
448
|
+
for (const id of ids) if (!imported.has(id)) await visit(id)
|
|
449
|
+
for (const id of ids) await visit(id)
|
|
450
|
+
|
|
451
|
+
const map = new Mapping.GenMapping({ file: 'zyzz.css' })
|
|
452
|
+
let line = 0
|
|
453
|
+
css = ''
|
|
454
|
+
for (const chunk of chunks) {
|
|
455
|
+
if (!chunk.code) continue
|
|
456
|
+
const traced = Mapping.fromMap(chunk.map)
|
|
457
|
+
for (const mapping of Mapping.allMappings(traced)) {
|
|
458
|
+
const generated = {
|
|
459
|
+
line: mapping.generated.line + line,
|
|
460
|
+
column: mapping.generated.column,
|
|
461
|
+
}
|
|
462
|
+
if (
|
|
463
|
+
mapping.source !== undefined &&
|
|
464
|
+
mapping.original !== undefined
|
|
465
|
+
) {
|
|
466
|
+
const location = {
|
|
467
|
+
generated,
|
|
468
|
+
original: mapping.original,
|
|
469
|
+
source: mapping.source.startsWith('app/')
|
|
470
|
+
? Path.join(root, mapping.source.slice(4))
|
|
471
|
+
: mapping.source,
|
|
472
|
+
}
|
|
473
|
+
if (mapping.name === undefined)
|
|
474
|
+
Mapping.addMapping(map, location)
|
|
475
|
+
else
|
|
476
|
+
Mapping.addMapping(map, { ...location, name: mapping.name })
|
|
477
|
+
} else Mapping.addMapping(map, { generated })
|
|
478
|
+
}
|
|
479
|
+
const encoded = Mapping.toEncodedMap(traced)
|
|
480
|
+
encoded.sources.forEach((source, index) => {
|
|
481
|
+
if (source !== null)
|
|
482
|
+
Mapping.setSourceContent(
|
|
483
|
+
map,
|
|
484
|
+
source.startsWith('app/')
|
|
485
|
+
? Path.join(root, source.slice(4))
|
|
486
|
+
: source,
|
|
487
|
+
encoded.sourcesContent?.[index] ?? null,
|
|
488
|
+
)
|
|
489
|
+
})
|
|
490
|
+
css += chunk.code + '\n'
|
|
491
|
+
line += chunk.code.split('\n').length
|
|
492
|
+
}
|
|
493
|
+
cssMap = JSON.stringify(Mapping.toEncodedMap(map))
|
|
494
|
+
script = Catalogs.scripts(
|
|
495
|
+
Object.values({ ...contracts, ...result.contracts }).flatMap(
|
|
496
|
+
Catalogs.read,
|
|
497
|
+
),
|
|
498
|
+
).join('\n')
|
|
499
|
+
graph = result
|
|
500
|
+
if (meta.framework === 'webpack') emit(this)
|
|
501
|
+
if (meta.framework === 'rollup')
|
|
502
|
+
for (const file of [...files, ...directories])
|
|
503
|
+
this.addWatchFile(file)
|
|
504
|
+
})()
|
|
505
|
+
await pending
|
|
506
|
+
},
|
|
507
|
+
enforce: 'pre',
|
|
508
|
+
esbuild: {
|
|
509
|
+
config(options) {
|
|
510
|
+
if (!options.outdir || options.write === false)
|
|
511
|
+
throw new Error(
|
|
512
|
+
'Zyzz requires esbuild outdir and write: true to emit CSS assets.',
|
|
513
|
+
)
|
|
514
|
+
options.outdir = Path.resolve(
|
|
515
|
+
options.absWorkingDir ?? process.cwd(),
|
|
516
|
+
options.outdir,
|
|
517
|
+
)
|
|
518
|
+
},
|
|
519
|
+
setup(build) {
|
|
520
|
+
// Unplugin forwards watchFiles but not watchDirs from its transform hook.
|
|
521
|
+
build.onLoad({ filter: sourcePattern }, async ({ path }) => {
|
|
522
|
+
await pending
|
|
523
|
+
if (!sources.has(path)) return
|
|
524
|
+
const output = transform(await Fs.readFile(path, 'utf8'), path)
|
|
525
|
+
if (!output) return
|
|
526
|
+
return {
|
|
527
|
+
contents: `${output.code}\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(output.map).toString('base64')}`,
|
|
528
|
+
loader: (() => {
|
|
529
|
+
if (path.endsWith('.tsx')) return 'tsx'
|
|
530
|
+
if (path.endsWith('.jsx')) return 'jsx'
|
|
531
|
+
if (/\.[cm]?ts$/.test(path)) return 'ts'
|
|
532
|
+
return 'js'
|
|
533
|
+
})(),
|
|
534
|
+
resolveDir: Path.dirname(path),
|
|
535
|
+
watchDirs: [...directories],
|
|
536
|
+
watchFiles: [...files],
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
name: 'zyzz',
|
|
542
|
+
rollup: {
|
|
543
|
+
shouldTransformCachedModule() {
|
|
544
|
+
return true
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
transform: {
|
|
548
|
+
filter: {
|
|
549
|
+
id: {
|
|
550
|
+
include: sourcePattern,
|
|
551
|
+
exclude:
|
|
552
|
+
/(?:^|[/\\])node_modules[/\\]|\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/,
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
async handler(code, id) {
|
|
556
|
+
await pending
|
|
557
|
+
const output = transform(code, id)
|
|
558
|
+
if (!output) return
|
|
559
|
+
for (const file of files) this.addWatchFile(file)
|
|
560
|
+
const native = this.getNativeBuildContext?.()
|
|
561
|
+
for (const directory of directories) {
|
|
562
|
+
if (native?.framework === 'webpack')
|
|
563
|
+
native.loaderContext?.addContextDependency(directory)
|
|
564
|
+
else if (meta.framework === 'rollup') this.addWatchFile(directory)
|
|
565
|
+
}
|
|
566
|
+
return output
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
webpack(compiler) {
|
|
570
|
+
compiler.hooks.watchRun.tap('zyzz', () => {
|
|
571
|
+
// Directory invalidations must also discard cached source reads before the graph and loaders rebuild.
|
|
572
|
+
for (const file of compiler.modifiedFiles ?? [])
|
|
573
|
+
if (directories.has(file)) compiler.inputFileSystem?.purge?.(file)
|
|
574
|
+
})
|
|
575
|
+
},
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
/** Bundler factories. Vite retains its native CSS delivery and environment-specific HMR. */
|
|
581
|
+
export const zyzz = {
|
|
582
|
+
/** Creates an esbuild plugin that emits stylesheets and watches source directories. */
|
|
583
|
+
esbuild: portable.esbuild,
|
|
584
|
+
/** Creates a Rollup plugin with dependency-aware cached transforms. */
|
|
585
|
+
rollup: portable.rollup,
|
|
586
|
+
/** Creates the existing Vite plugin with automatic CSS delivery and HMR. */
|
|
587
|
+
vite: Vite.zyzz,
|
|
588
|
+
/** Creates a Webpack plugin that compiles before language loaders. */
|
|
589
|
+
webpack: portable.webpack,
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function inside(parent: string, child: string) {
|
|
593
|
+
const relative = Path.relative(parent, child)
|
|
594
|
+
return (
|
|
595
|
+
relative === '' ||
|
|
596
|
+
(relative !== '..' &&
|
|
597
|
+
!relative.startsWith(`..${Path.sep}`) &&
|
|
598
|
+
!Path.isAbsolute(relative))
|
|
599
|
+
)
|
|
600
|
+
}
|