diff --git a/packages/sv-utils/api-surface.md b/packages/sv-utils/api-surface.md index 7f8a9a8ba..1e5050d6e 100644 --- a/packages/sv-utils/api-surface.md +++ b/packages/sv-utils/api-surface.md @@ -3,69 +3,6 @@ ```ts -type Agent = 'npm' | 'yarn' | 'yarn@berry' | 'pnpm' | 'pnpm@6' | 'bun' | 'deno'; -type AgentName = 'npm' | 'yarn' | 'pnpm' | 'bun' | 'deno'; -type AgentCommandValue = (string | number)[] | ((args: string[]) => string[]) | null; -interface AgentCommands { - agent: AgentCommandValue; - run: AgentCommandValue; - install: AgentCommandValue; - frozen: AgentCommandValue; - global: AgentCommandValue; - add: AgentCommandValue; - upgrade: AgentCommandValue; - 'upgrade-interactive': AgentCommandValue; - dedupe: AgentCommandValue; - execute: AgentCommandValue; - 'execute-local': AgentCommandValue; - uninstall: AgentCommandValue; - global_uninstall: AgentCommandValue; -} -type Command = keyof AgentCommands; -interface ResolvedCommand { - command: string; - - args: string[]; -} -type DetectStrategy = 'lockfile' | 'packageManager-field' | 'devEngines-field' | 'install-metadata'; -interface DetectOptions { - cwd?: string; - - strategies?: DetectStrategy[]; - - onUnknown?: (packageManager: string) => DetectResult | null | undefined; - - stopDir?: string | ((currentDir: string) => boolean); - - packageJsonParser?: (content: string, filepath: string) => any | Promise; -} -interface DetectResult { - name: AgentName; - - agent: Agent; - - version?: string; -} -declare const COMMANDS: { - npm: AgentCommands; - yarn: AgentCommands; - 'yarn@berry': AgentCommands; - pnpm: AgentCommands; - 'pnpm@6': AgentCommands; - bun: AgentCommands; - deno: AgentCommands; -}; - -declare function resolveCommand( - agent: Agent, - command: Command, - args: string[] -): ResolvedCommand | null; - -declare function constructCommand(value: AgentCommandValue, args: string[]): ResolvedCommand | null; -declare const AGENTS: Agent[]; - -declare function detect(options?: DetectOptions): Promise; /*! * Copyright (c) Squirrel Chat et al., All rights reserved. * SPDX-License-Identifier: BSD-3-Clause @@ -112,765 +49,6 @@ type TomlTable = { [key: string]: TomlValue; }; type TomlValue = TomlPrimitive | TomlValue[] | TomlTable; - -declare class LineCounter { - lineStarts: number[]; - - addNewLine: (offset: number) => number; - - linePos: (offset: number) => { - line: number; - col: number; - }; -} -type ErrorCode = - | 'ALIAS_PROPS' - | 'BAD_ALIAS' - | 'BAD_DIRECTIVE' - | 'BAD_DQ_ESCAPE' - | 'BAD_INDENT' - | 'BAD_PROP_ORDER' - | 'BAD_SCALAR_START' - | 'BLOCK_AS_IMPLICIT_KEY' - | 'BLOCK_IN_FLOW' - | 'DUPLICATE_KEY' - | 'IMPOSSIBLE' - | 'KEY_OVER_1024_CHARS' - | 'MISSING_CHAR' - | 'MULTILINE_IMPLICIT_KEY' - | 'MULTIPLE_ANCHORS' - | 'MULTIPLE_DOCS' - | 'MULTIPLE_TAGS' - | 'NON_STRING_KEY' - | 'TAB_AS_INDENT' - | 'TAG_RESOLVE_FAILED' - | 'UNEXPECTED_TOKEN' - | 'BAD_COLLECTION_TYPE'; -type LinePos = { - line: number; - col: number; -}; -declare class YAMLError extends Error { - name: 'YAMLParseError' | 'YAMLWarning'; - code: ErrorCode; - message: string; - pos: [number, number]; - linePos?: [LinePos] | [LinePos, LinePos]; - constructor(name: YAMLError['name'], pos: [number, number], code: ErrorCode, message: string); -} -declare class YAMLWarning extends YAMLError { - constructor(pos: [number, number], code: ErrorCode, message: string); -} -type Reviver = (key: unknown, value: unknown) => unknown; -type LogLevelId = 'silent' | 'error' | 'warn' | 'debug'; -interface AnchorData { - aliasCount: number; - count: number; - res: unknown; -} -interface ToJSContext { - anchors: Map; - - aliasResolveCache?: Node[]; - doc: Document; - keep: boolean; - mapAsMap: boolean; - mapKeyWarned: boolean; - maxAliasCount: number; - onCreate?: (res: unknown) => void; -} -declare namespace Scalar { - interface Parsed extends Scalar { - range: Range; - source: string; - srcToken?: FlowScalar | BlockScalar; - } - type BLOCK_FOLDED = 'BLOCK_FOLDED'; - type BLOCK_LITERAL = 'BLOCK_LITERAL'; - type PLAIN = 'PLAIN'; - type QUOTE_DOUBLE = 'QUOTE_DOUBLE'; - type QUOTE_SINGLE = 'QUOTE_SINGLE'; - type Type = BLOCK_FOLDED | BLOCK_LITERAL | PLAIN | QUOTE_DOUBLE | QUOTE_SINGLE; -} -declare class Scalar extends NodeBase { - static readonly BLOCK_FOLDED = 'BLOCK_FOLDED'; - static readonly BLOCK_LITERAL = 'BLOCK_LITERAL'; - static readonly PLAIN = 'PLAIN'; - static readonly QUOTE_DOUBLE = 'QUOTE_DOUBLE'; - static readonly QUOTE_SINGLE = 'QUOTE_SINGLE'; - value: T; - - anchor?: string; - - format?: string; - - minFractionDigits?: number; - - source?: string; - - type?: Scalar.Type; - constructor(value: T); - toJSON(arg?: any, ctx?: ToJSContext): any; - toString(): string; -} -type StringifyContext = { - actualString?: boolean; - allNullValues?: boolean; - anchors: Set; - doc: Document; - forceBlockIndent?: boolean; - implicitKey?: boolean; - indent: string; - indentStep: string; - indentAtStart?: number; - inFlow: boolean | null; - inStringifyKey?: boolean; - flowCollectionPadding: string; - options: Readonly>>; - resolvedAliases?: Set; -}; -declare abstract class Collection extends NodeBase { - schema: Schema | undefined; - [NODE_TYPE]: symbol; - items: unknown[]; - - anchor?: string; - - flow?: boolean; - constructor(type: symbol, schema?: Schema); - - clone(schema?: Schema): Collection; - - abstract add(value: unknown): void; - - abstract delete(key: unknown): boolean; - - abstract get(key: unknown, keepScalar?: boolean): unknown; - - abstract has(key: unknown): boolean; - - abstract set(key: unknown, value: unknown): void; - - addIn(path: Iterable, value: unknown): void; - - deleteIn(path: Iterable): boolean; - - getIn(path: Iterable, keepScalar?: boolean): unknown; - hasAllNullValues(allowScalar?: boolean): boolean; - - hasIn(path: Iterable): boolean; - - setIn(path: Iterable, value: unknown): void; -} -declare namespace YAMLSeq { - interface Parsed< - T extends ParsedNode | Pair = ParsedNode - > extends YAMLSeq { - items: T[]; - range: Range; - srcToken?: BlockSequence | FlowCollection; - } -} -declare class YAMLSeq extends Collection { - static get tagName(): 'tag:yaml.org,2002:seq'; - items: T[]; - constructor(schema?: Schema); - add(value: T): void; - - delete(key: unknown): boolean; - - get(key: unknown, keepScalar: true): Scalar | undefined; - get(key: unknown, keepScalar?: false): T | undefined; - get(key: unknown, keepScalar?: boolean): T | Scalar | undefined; - - has(key: unknown): boolean; - - set(key: unknown, value: T): void; - toJSON(_?: unknown, ctx?: ToJSContext): unknown[]; - toString(ctx?: StringifyContext, onComment?: () => void, onChompKeep?: () => void): string; - static from(schema: Schema, obj: unknown, ctx: CreateNodeContext): YAMLSeq; -} -interface TagBase { - createNode?: (schema: Schema, value: unknown, ctx: CreateNodeContext) => Node; - - default?: boolean | 'key'; - - format?: string; - - identify?: (value: unknown) => boolean; - - tag: string; -} -interface ScalarTag extends TagBase { - collection?: never; - nodeClass?: never; - - resolve(value: string, onError: (message: string) => void, options: ParseOptions): unknown; - - stringify?: ( - item: Scalar, - ctx: StringifyContext, - onComment?: () => void, - onChompKeep?: () => void - ) => string; - - test?: RegExp; -} -interface CollectionTag extends TagBase { - stringify?: never; - test?: never; - - collection: 'map' | 'seq'; - - nodeClass?: { - new (schema?: Schema): Node; - from?: (schema: Schema, obj: unknown, ctx: CreateNodeContext) => Node; - }; - - resolve?: ( - value: YAMLMap.Parsed | YAMLSeq.Parsed, - onError: (message: string) => void, - options: ParseOptions - ) => unknown; -} -type MapLike = Map | Set | Record; -declare namespace YAMLMap { - interface Parsed< - K extends ParsedNode = ParsedNode, - V extends ParsedNode | null = ParsedNode | null - > extends YAMLMap { - items: Pair[]; - range: Range; - srcToken?: BlockMap | FlowCollection; - } -} -declare class YAMLMap extends Collection { - static get tagName(): 'tag:yaml.org,2002:map'; - items: Pair[]; - constructor(schema?: Schema); - - static from(schema: Schema, obj: unknown, ctx: CreateNodeContext): YAMLMap; - - add( - pair: - | Pair - | { - key: K; - value: V; - }, - overwrite?: boolean - ): void; - delete(key: unknown): boolean; - get(key: unknown, keepScalar: true): Scalar | undefined; - get(key: unknown, keepScalar?: false): V | undefined; - get(key: unknown, keepScalar?: boolean): V | Scalar | undefined; - has(key: unknown): boolean; - set(key: K, value: V): void; - - toJSON>( - _?: unknown, - ctx?: ToJSContext, - Type?: { - new (): T; - } - ): any; - toString(ctx?: StringifyContext, onComment?: () => void, onChompKeep?: () => void): string; -} -declare const MAP: unique symbol; -declare const SCALAR: unique symbol; -declare const SEQ: unique symbol; -declare const NODE_TYPE: unique symbol; -declare class Schema { - compat: Array | null; - knownTags: Record; - name: string; - sortMapEntries: ((a: Pair, b: Pair) => number) | null; - tags: Array; - toStringOptions: Readonly | null; - readonly [MAP]: CollectionTag; - readonly [SCALAR]: ScalarTag; - readonly [SEQ]: CollectionTag; - constructor({ - compat, - customTags, - merge, - resolveKnownTags, - schema, - sortMapEntries, - toStringDefaults - }: SchemaOptions); - clone(): Schema; -} -interface CreateNodeContext { - aliasDuplicateObjects: boolean; - keepUndefined: boolean; - onAnchor: (source: unknown) => string; - onTagObj?: (tagObj: ScalarTag | CollectionTag) => void; - sourceObjects: Map< - unknown, - { - anchor: string | null; - node: Node | null; - } - >; - replacer?: Replacer; - schema: Schema; -} -declare function addPairToJSMap( - ctx: ToJSContext | undefined, - map: MapLike, - { key, value }: Pair -): MapLike; -declare class Pair { - readonly [NODE_TYPE]: symbol; - - key: K; - - value: V | null; - - srcToken?: CollectionItem; - constructor(key: K, value?: V | null); - clone(schema?: Schema): Pair; - toJSON(_?: unknown, ctx?: ToJSContext): ReturnType; - toString(ctx?: StringifyContext, onComment?: () => void, onChompKeep?: () => void): string; -} -declare const tagsByName: { - binary: ScalarTag; - bool: ScalarTag & { - test: RegExp; - }; - float: ScalarTag; - floatExp: ScalarTag; - floatNaN: ScalarTag; - floatTime: ScalarTag; - int: ScalarTag; - intHex: ScalarTag; - intOct: ScalarTag; - intTime: ScalarTag; - map: CollectionTag; - merge: ScalarTag & { - identify(value: unknown): boolean; - test: RegExp; - }; - null: ScalarTag & { - test: RegExp; - }; - omap: CollectionTag; - pairs: CollectionTag; - seq: CollectionTag; - set: CollectionTag; - timestamp: ScalarTag & { - test: RegExp; - }; -}; -type TagId = keyof typeof tagsByName; -type Tags = Array; -type ParseOptions = { - intAsBigInt?: boolean; - - keepSourceTokens?: boolean; - - lineCounter?: LineCounter; - - prettyErrors?: boolean; - - strict?: boolean; - - stringKeys?: boolean; - - uniqueKeys?: boolean | ((a: ParsedNode, b: ParsedNode) => boolean); -}; -type DocumentOptions = { - _directives?: Directives; - - logLevel?: LogLevelId; - - version?: '1.1' | '1.2' | 'next'; -}; -type SchemaOptions = { - compat?: string | Tags | null; - - customTags?: Tags | ((tags: Tags) => Tags) | null; - - merge?: boolean; - - resolveKnownTags?: boolean; - - schema?: string | Schema; - - sortMapEntries?: boolean | ((a: Pair, b: Pair) => number); - - toStringDefaults?: ToStringOptions; -}; -type CreateNodeOptions = { - aliasDuplicateObjects?: boolean; - - anchorPrefix?: string; - flow?: boolean; - - keepUndefined?: boolean | null; - onTagObj?: (tagObj: ScalarTag | CollectionTag) => void; - - tag?: string; -}; -type ToJSOptions = { - mapAsMap?: boolean; - - maxAliasCount?: number; - - onAnchor?: (value: unknown, count: number) => void; - - reviver?: Reviver; -}; -type ToStringOptions = { - blockQuote?: boolean | 'folded' | 'literal'; - - collectionStyle?: 'any' | 'block' | 'flow'; - - commentString?: (comment: string) => string; - - defaultKeyType?: Scalar.Type | null; - - defaultStringType?: Scalar.Type; - - directives?: boolean | null; - - doubleQuotedAsJSON?: boolean; - - doubleQuotedMinMultiLineLength?: number; - - falseStr?: string; - - flowCollectionPadding?: boolean; - - indent?: number; - - indentSeq?: boolean; - - lineWidth?: number; - - minContentWidth?: number; - - nullStr?: string; - - simpleKeys?: boolean; - - singleQuote?: boolean | null; - - trueStr?: string; - - verifyAliasOrder?: boolean; -}; -type Node = Alias | Scalar | YAMLMap | YAMLSeq; - -type NodeType = T extends string | number | bigint | boolean | null | undefined - ? Scalar - : T extends Date - ? Scalar - : T extends Array - ? YAMLSeq> - : T extends { - [key: string]: any; - } - ? YAMLMap, NodeType> - : T extends { - [key: number]: any; - } - ? YAMLMap, NodeType> - : Node; -type ParsedNode = Alias.Parsed | Scalar.Parsed | YAMLMap.Parsed | YAMLSeq.Parsed; - -type Range = [number, number, number]; -declare abstract class NodeBase { - readonly [NODE_TYPE]: symbol; - - comment?: string | null; - - commentBefore?: string | null; - - range?: Range | null; - - spaceBefore?: boolean; - - srcToken?: Token; - - tag?: string; - - addToJSMap?: (ctx: ToJSContext | undefined, map: MapLike, value: unknown) => void; - - abstract toJSON(): any; - abstract toString( - ctx?: StringifyContext, - onComment?: () => void, - onChompKeep?: () => void - ): string; - constructor(type: symbol); - - clone(): NodeBase; - - toJS( - doc: Document, - { mapAsMap, maxAliasCount, onAnchor, reviver }?: ToJSOptions - ): any; -} -interface SourceToken { - type: - | 'byte-order-mark' - | 'doc-mode' - | 'doc-start' - | 'space' - | 'comment' - | 'newline' - | 'directive-line' - | 'anchor' - | 'tag' - | 'seq-item-ind' - | 'explicit-key-ind' - | 'map-value-ind' - | 'flow-map-start' - | 'flow-map-end' - | 'flow-seq-start' - | 'flow-seq-end' - | 'flow-error-end' - | 'comma' - | 'block-scalar-header'; - offset: number; - indent: number; - source: string; -} -interface ErrorToken { - type: 'error'; - offset: number; - source: string; - message: string; -} -interface Directive$1 { - type: 'directive'; - offset: number; - source: string; -} -interface Document$1 { - type: 'document'; - offset: number; - start: SourceToken[]; - value?: Token; - end?: SourceToken[]; -} -interface DocumentEnd { - type: 'doc-end'; - offset: number; - source: string; - end?: SourceToken[]; -} -interface FlowScalar { - type: 'alias' | 'scalar' | 'single-quoted-scalar' | 'double-quoted-scalar'; - offset: number; - indent: number; - source: string; - end?: SourceToken[]; -} -interface BlockScalar { - type: 'block-scalar'; - offset: number; - indent: number; - props: Token[]; - source: string; -} -interface BlockMap { - type: 'block-map'; - offset: number; - indent: number; - items: Array< - | { - start: SourceToken[]; - explicitKey?: true; - key?: never; - sep?: never; - value?: never; - } - | { - start: SourceToken[]; - explicitKey?: true; - key: Token | null; - sep: SourceToken[]; - value?: Token; - } - >; -} -interface BlockSequence { - type: 'block-seq'; - offset: number; - indent: number; - items: Array<{ - start: SourceToken[]; - key?: never; - sep?: never; - value?: Token; - }>; -} -type CollectionItem = { - start: SourceToken[]; - key?: Token | null; - sep?: SourceToken[]; - value?: Token; -}; -interface FlowCollection { - type: 'flow-collection'; - offset: number; - indent: number; - start: SourceToken; - items: CollectionItem[]; - end: SourceToken[]; -} -type Token = - | SourceToken - | ErrorToken - | Directive$1 - | Document$1 - | DocumentEnd - | FlowScalar - | BlockScalar - | BlockMap - | BlockSequence - | FlowCollection; -declare namespace Alias { - interface Parsed extends Alias { - range: Range; - srcToken?: FlowScalar & { - type: 'alias'; - }; - } -} -declare class Alias extends NodeBase { - source: string; - anchor?: never; - constructor(source: string); - - resolve(doc: Document, ctx?: ToJSContext): Scalar | YAMLMap | YAMLSeq | undefined; - toJSON(_arg?: unknown, ctx?: ToJSContext): unknown; - toString(ctx?: StringifyContext, _onComment?: () => void, _onChompKeep?: () => void): string; -} -type Replacer = any[] | ((key: any, value: any) => unknown); -declare namespace Document { - interface Parsed< - Contents extends ParsedNode = ParsedNode, - Strict extends boolean = true - > extends Document { - directives: Directives; - range: Range; - } -} -declare class Document { - readonly [NODE_TYPE]: symbol; - - commentBefore: string | null; - - comment: string | null; - - contents: Strict extends true ? Contents | null : Contents; - directives: Strict extends true ? Directives | undefined : Directives; - - errors: YAMLError[]; - options: Required< - Omit - >; - - range?: Range; - - schema: Schema; - - warnings: YAMLWarning[]; - - constructor( - value?: any, - options?: DocumentOptions & SchemaOptions & ParseOptions & CreateNodeOptions - ); - constructor( - value: any, - replacer: null | Replacer, - options?: DocumentOptions & SchemaOptions & ParseOptions & CreateNodeOptions - ); - - clone(): Document; - - add(value: any): void; - - addIn(path: Iterable, value: unknown): void; - - createAlias(node: Strict extends true ? Scalar | YAMLMap | YAMLSeq : Node, name?: string): Alias; - - createNode(value: T, options?: CreateNodeOptions): NodeType; - createNode( - value: T, - replacer: Replacer | CreateNodeOptions | null, - options?: CreateNodeOptions - ): NodeType; - - createPair( - key: unknown, - value: unknown, - options?: CreateNodeOptions - ): Pair; - - delete(key: unknown): boolean; - - deleteIn(path: Iterable | null): boolean; - - get(key: unknown, keepScalar?: boolean): Strict extends true ? unknown : any; - - getIn(path: Iterable | null, keepScalar?: boolean): Strict extends true ? unknown : any; - - has(key: unknown): boolean; - - hasIn(path: Iterable | null): boolean; - - set(key: any, value: unknown): void; - - setIn(path: Iterable | null, value: unknown): void; - - setSchema(version: '1.1' | '1.2' | 'next' | null, options?: SchemaOptions): void; - - toJS( - opt?: ToJSOptions & { - [ignored: string]: unknown; - } - ): any; - - toJSON(jsonArg?: string | null, onAnchor?: ToJSOptions['onAnchor']): any; - - toString(options?: ToStringOptions): string; -} -declare class Directives { - static defaultYaml: Directives['yaml']; - static defaultTags: Directives['tags']; - yaml: { - version: '1.1' | '1.2' | 'next'; - explicit?: boolean; - }; - tags: Record; - - docStart: true | null; - - docEnd: boolean; - - private atNextDocument?; - constructor(yaml?: Directives['yaml'], tags?: Directives['tags']); - clone(): Directives; - - atDocument(): Directives; - - add(line: string, onError: (offset: number, message: string, warning?: boolean) => void): boolean; - - tagName(source: string, onError: (message: string) => void): string | null; - - tagString(tag: string): string; - toString(doc?: Document): string; -} - -declare function parseDocument( - source: string, - options?: ParseOptions & DocumentOptions & SchemaOptions -): Contents extends ParsedNode ? Document.Parsed : Document; declare module 'estree' { interface TSTypeAnnotation { type: 'TSTypeAnnotation'; @@ -966,7 +144,6 @@ declare module 'estree' { importKind: 'type' | 'value'; } } -declare function parseYaml$1(content: string): ReturnType; type CommentType = { type: 'Line' | 'Block'; value: string; @@ -985,6 +162,11 @@ declare class Comments { ): void; remove(predicate: (comment: estree.Comment) => boolean | undefined | null): void; } + +type YamlDocument = { + get(key: string): unknown; + set(key: string, value: unknown): void; +}; type ParseBase = { source: string; @@ -1004,7 +186,7 @@ declare function parseJson(source: string): { data: any; } & ParseBase; declare function parseYaml(source: string): { - data: ReturnType; + data: YamlDocument; } & ParseBase; declare function parseSvelte(source: string): { ast: SvelteAst.Root; @@ -1012,17 +194,11 @@ declare function parseSvelte(source: string): { declare function parseToml(source: string): { data: TomlTable; } & ParseBase; -interface DedentOptions { - alignValues?: boolean; - escapeSpecialCharacters?: boolean; - trimWhitespace?: boolean; -} -interface Dedent { - (literals: string): string; + +type Dedent = { (strings: TemplateStringsArray, ...values: unknown[]): string; - withOptions: CreateDedent; -} -type CreateDedent = (options: DedentOptions) => Dedent; + (source: string): string; +}; declare const dedent: Dedent; declare module 'zimmerframe' { export function walk< @@ -1057,6 +233,7 @@ declare module 'zimmerframe' { } export {}; } //# sourceMappingURL=index.d.ts.map +declare function resolveCommandArray(agent: Agent, command: Command, args: string[]): string[]; declare namespace index_d_exports$1 { export { addAtRule, addDeclaration, addImports, addRule }; } @@ -1512,7 +689,7 @@ declare const transforms: { ): TransformFn; yaml( - cb: (file: { data: ReturnType['data']; content: string }) => void | false, + cb: (file: { data: YamlDocument; content: string }) => void | false, options?: TransformOptions ): TransformFn; @@ -1565,22 +742,6 @@ type Package = { keywords?: string[]; workspaces?: string[]; }; -declare function getPackageJson(cwd: string): { - source: string; - data: Package; - generateCode: () => string; -}; -declare function readFile(cwd: string, filePath: string): string; -declare function fileExists(cwd: string, filePath: string): boolean; -declare function writeFile(cwd: string, filePath: string, content: string): void; -declare function installPackages( - dependencies: Array<{ - pkg: string; - version: string; - dev: boolean; - }>, - cwd: string -): string; declare const commonFilePaths: { readonly packageJson: 'package.json'; readonly svelteConfig: 'svelte.config.js'; @@ -1590,6 +751,28 @@ declare const commonFilePaths: { readonly viteConfig: 'vite.config.js'; readonly viteConfigTS: 'vite.config.ts'; }; +declare function fileExists(cwd: string, filePath: string): boolean; + +declare function loadFile(cwd: string, filePath: string): string; + +declare function saveFile(cwd: string, filePath: string, content: string): void; +declare function loadPackageJson(cwd: string): { + source: string; + data: Package; + generateCode: () => string; +}; +/** + * @deprecated Use {@link loadFile} instead. This alias will be removed in a future version. + */ +declare const readFile: typeof loadFile; +/** + * @deprecated Use {@link saveFile} instead. This alias will be removed in a future version. + */ +declare const writeFile: typeof saveFile; +/** + * @deprecated Use {@link loadPackageJson} instead. This alias will be removed in a future version. + */ +declare const getPackageJson: typeof loadPackageJson; type ColorInput = string | string[]; declare const color: { addon: (str: ColorInput) => string; @@ -1606,8 +789,6 @@ declare const color: { hidden: (str: ColorInput) => string; }; -declare function resolveCommandArray(agent: Agent, command: Command, args: string[]): string[]; - declare const parse: { css: typeof parseCss; html: typeof parseHtml; @@ -1627,6 +808,7 @@ export { type SvelteAst, type TransformFn, index_d_exports as Walker, + type YamlDocument, color, commonFilePaths, constructCommand, @@ -1638,16 +820,18 @@ export { fileExists, getPackageJson, index_d_exports$2 as html, - installPackages, isVersionUnsupportedBelow, index_d_exports$3 as js, json_d_exports as json, + loadFile, + loadPackageJson, parse, pnpm_d_exports as pnpm, readFile, resolveCommand, resolveCommandArray, sanitizeName, + saveFile, splitVersion, index_d_exports$4 as svelte, text_d_exports as text, diff --git a/packages/sv-utils/src/dedent.ts b/packages/sv-utils/src/dedent.ts new file mode 100644 index 000000000..1210bdac9 --- /dev/null +++ b/packages/sv-utils/src/dedent.ts @@ -0,0 +1,13 @@ +import dedentImpl from 'dedent'; + +/** + * Template-tag or single-string dedent helper (same behavior as the `dedent` package). + * Types are hand-written so the public `.d.mts` does not inline `dedent`'s full declarations. + */ +export type Dedent = { + (strings: TemplateStringsArray, ...values: unknown[]): string; + (source: string): string; +}; + +const dedent: Dedent = dedentImpl as Dedent; +export default dedent; diff --git a/packages/sv-utils/src/files.ts b/packages/sv-utils/src/files.ts index 4dd22c359..8b6dc2488 100644 --- a/packages/sv-utils/src/files.ts +++ b/packages/sv-utils/src/files.ts @@ -13,22 +13,23 @@ export type Package = { workspaces?: string[]; }; -export function getPackageJson(cwd: string): { - source: string; - data: Package; - generateCode: () => string; -} { - const packageText = readFile(cwd, commonFilePaths.packageJson); - if (!packageText) { - const pkgPath = path.join(cwd, commonFilePaths.packageJson); - throw new Error(`Invalid workspace: missing '${pkgPath}'`); - } +export const commonFilePaths = { + packageJson: 'package.json', + svelteConfig: 'svelte.config.js', + svelteConfigTS: 'svelte.config.ts', + jsconfig: 'jsconfig.json', + tsconfig: 'tsconfig.json', + viteConfig: 'vite.config.js', + viteConfigTS: 'vite.config.ts' +} as const; - const { data, generateCode } = parseJson(packageText); - return { source: packageText, data: data as Package, generateCode }; +export function fileExists(cwd: string, filePath: string): boolean { + const fullFilePath = path.resolve(cwd, filePath); + return fs.existsSync(fullFilePath); } -export function readFile(cwd: string, filePath: string): string { +/** Synchronous load of a workspace-relative file as UTF-8 text; missing files yield `''`. */ +export function loadFile(cwd: string, filePath: string): string { const fullFilePath = path.resolve(cwd, filePath); if (!fileExists(cwd, filePath)) { @@ -40,12 +41,8 @@ export function readFile(cwd: string, filePath: string): string { return text; } -export function fileExists(cwd: string, filePath: string): boolean { - const fullFilePath = path.resolve(cwd, filePath); - return fs.existsSync(fullFilePath); -} - -export function writeFile(cwd: string, filePath: string, content: string): void { +/** Synchronous write of a workspace-relative file (creates parent dirs). */ +export function saveFile(cwd: string, filePath: string, content: string): void { const fullFilePath = path.resolve(cwd, filePath); const fullDirectoryPath = path.dirname(fullFilePath); @@ -58,44 +55,32 @@ export function writeFile(cwd: string, filePath: string, content: string): void fs.writeFileSync(fullFilePath, content, 'utf8'); } -export function installPackages( - dependencies: Array<{ pkg: string; version: string; dev: boolean }>, - cwd: string -): string { - const { data, generateCode } = getPackageJson(cwd); - - for (const dependency of dependencies) { - if (dependency.dev) { - data.devDependencies ??= {}; - data.devDependencies[dependency.pkg] = dependency.version; - } else { - data.dependencies ??= {}; - data.dependencies[dependency.pkg] = dependency.version; - } +export function loadPackageJson(cwd: string): { + source: string; + data: Package; + generateCode: () => string; +} { + const packageText = loadFile(cwd, commonFilePaths.packageJson); + if (!packageText) { + const pkgPath = path.join(cwd, commonFilePaths.packageJson); + throw new Error(`Invalid workspace: missing '${pkgPath}'`); } - if (data.dependencies) data.dependencies = alphabetizeProperties(data.dependencies); - if (data.devDependencies) data.devDependencies = alphabetizeProperties(data.devDependencies); - - writeFile(cwd, commonFilePaths.packageJson, generateCode()); - return commonFilePaths.packageJson; + const { data, generateCode } = parseJson(packageText); + return { source: packageText, data: data as Package, generateCode }; } -function alphabetizeProperties(obj: Record) { - const orderedObj: Record = {}; - const sortedEntries = Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)); - for (const [key, value] of sortedEntries) { - orderedObj[key] = value; - } - return orderedObj; -} +/** + * @deprecated Use {@link loadFile} instead. This alias will be removed in a future version. + */ +export const readFile: typeof loadFile = loadFile; -export const commonFilePaths = { - packageJson: 'package.json', - svelteConfig: 'svelte.config.js', - svelteConfigTS: 'svelte.config.ts', - jsconfig: 'jsconfig.json', - tsconfig: 'tsconfig.json', - viteConfig: 'vite.config.js', - viteConfigTS: 'vite.config.ts' -} as const; +/** + * @deprecated Use {@link saveFile} instead. This alias will be removed in a future version. + */ +export const writeFile: typeof saveFile = saveFile; + +/** + * @deprecated Use {@link loadPackageJson} instead. This alias will be removed in a future version. + */ +export const getPackageJson: typeof loadPackageJson = loadPackageJson; diff --git a/packages/sv-utils/src/index.ts b/packages/sv-utils/src/index.ts index 64c7b21e2..d8615802c 100644 --- a/packages/sv-utils/src/index.ts +++ b/packages/sv-utils/src/index.ts @@ -1,8 +1,3 @@ -import { - resolveCommand as _resolveCommand, - type Agent, - type Command -} from 'package-manager-detector'; import { parseCss, parseHtml, @@ -14,22 +9,19 @@ import { } from './tooling/parsers.ts'; // External re-exports -export { default as dedent } from 'dedent'; +export { default as dedent } from './dedent.ts'; export * as Walker from 'zimmerframe'; + +// Package managers (delegates to `package-manager-detector`; see `pm.ts`) export { AGENTS, type AgentName, COMMANDS, constructCommand, detect, - resolveCommand -} from 'package-manager-detector'; - -/** Resolves a package manager command and returns it as a string array (command + args). */ -export function resolveCommandArray(agent: Agent, command: Command, args: string[]): string[] { - const cmd = _resolveCommand(agent, command, args)!; - return [cmd.command, ...cmd.args]; -} + resolveCommand, + resolveCommandArray +} from './pm.ts'; // Parsing & language namespaces export * as css from './tooling/css/index.ts'; @@ -80,20 +72,33 @@ export { createPrinter } from './utils.ts'; export { sanitizeName } from './sanitize.ts'; export { downloadJson } from './downloadJson.ts'; -// File system helpers +// File system helpers (sync, workspace-relative paths) export { commonFilePaths, fileExists, - getPackageJson, - installPackages, - readFile, - writeFile, + loadFile, + loadPackageJson, + saveFile, type Package } from './files.ts'; +/** + * @deprecated Use {@link loadFile} instead. This alias will be removed in a future version. + */ +export { readFile } from './files.ts'; +/** + * @deprecated Use {@link saveFile} instead. This alias will be removed in a future version. + */ +export { writeFile } from './files.ts'; +/** + * @deprecated Use {@link loadPackageJson} instead. This alias will be removed in a future version. + */ +export { getPackageJson } from './files.ts'; + // Terminal styling export { color } from './color.ts'; // Types export type { Comments, AstTypes, SvelteAst } from './tooling/index.ts'; export type { TransformFn } from './tooling/transforms.ts'; +export type { YamlDocument } from './tooling/parsers.ts'; diff --git a/packages/sv-utils/src/pm.ts b/packages/sv-utils/src/pm.ts new file mode 100644 index 000000000..660b23c6a --- /dev/null +++ b/packages/sv-utils/src/pm.ts @@ -0,0 +1,24 @@ +/** + * Thin wrapper around [`package-manager-detector`](https://github.com/antfu/package-manager-detector). + * Only the symbols re-exported from the package root are public — we keep this module small and + * avoid exposing the full upstream surface. + */ +import { + resolveCommand as _resolveCommand, + type Agent, + type Command +} from 'package-manager-detector'; + +export { + AGENTS, + type AgentName, + COMMANDS, + constructCommand, + detect, + resolveCommand +} from 'package-manager-detector'; + +export function resolveCommandArray(agent: Agent, command: Command, args: string[]): string[] { + const cmd = _resolveCommand(agent, command, args)!; + return [cmd.command, ...cmd.args]; +} diff --git a/packages/sv-utils/src/tooling/parsers.ts b/packages/sv-utils/src/tooling/parsers.ts index f5661ebc2..6ae53f255 100644 --- a/packages/sv-utils/src/tooling/parsers.ts +++ b/packages/sv-utils/src/tooling/parsers.ts @@ -1,6 +1,15 @@ import type { TomlTable } from 'smol-toml'; import * as utils from './index.ts'; +/** + * Minimal shape for YAML document roots from `parse.yaml` — avoids re-exporting the full `yaml` types. + * At runtime this is the library’s document type; only `get` / `set` are part of the public contract. + */ +export type YamlDocument = { + get(key: string): unknown; + set(key: string, value: unknown): void; +}; + type ParseBase = { source: string; /** @@ -52,14 +61,12 @@ export function parseJson(source: string): { data: any } & ParseBase { return { data, source, generateCode }; } -export function parseYaml( - source: string -): { data: ReturnType } & ParseBase { +export function parseYaml(source: string): { data: YamlDocument } & ParseBase { if (!source) source = ''; const data = utils.parseYaml(source); - const generateCode = () => utils.serializeYaml(data); + const generateCode = () => utils.serializeYaml(data as Parameters[0]); - return { data, source, generateCode }; + return { data: data as YamlDocument, source, generateCode }; } export function parseSvelte(source: string): { ast: utils.SvelteAst.Root } & ParseBase { diff --git a/packages/sv-utils/src/tooling/transforms.ts b/packages/sv-utils/src/tooling/transforms.ts index 5d27c1696..361b5925b 100644 --- a/packages/sv-utils/src/tooling/transforms.ts +++ b/packages/sv-utils/src/tooling/transforms.ts @@ -12,7 +12,8 @@ import { parseScript, parseSvelte, parseToml, - parseYaml + parseYaml, + type YamlDocument } from './parsers.ts'; import { type RootWithInstance, ensureScript } from './svelte/index.ts'; import * as svelteNs from './svelte/index.ts'; @@ -190,7 +191,7 @@ export const transforms = { * Return `false` from the callback to abort - the original content is returned unchanged. */ yaml( - cb: (file: { data: ReturnType['data']; content: string }) => void | false, + cb: (file: { data: YamlDocument; content: string }) => void | false, options?: TransformOptions ): TransformFn { return (content) => { diff --git a/packages/sv/api-surface-testing.md b/packages/sv/api-surface-testing.md index 8d87dae41..6295a1d41 100644 --- a/packages/sv/api-surface-testing.md +++ b/packages/sv/api-surface-testing.md @@ -15,6 +15,7 @@ type SetupOptions = { variants: readonly ProjectVariant[]; clean?: boolean; }; +/** @deprecated Internal helper used by `createSetupTest` - will be removed from public API in a future version. */ declare function setup({ cwd, clean, variants }: SetupOptions): { templatesDir: string; }; @@ -23,11 +24,13 @@ type CreateOptions = { testName: string; templatesDir: string; }; +/** @deprecated Internal helper used by `createSetupTest` - will be removed from public API in a future version. */ declare function createProject({ cwd, testName, templatesDir }: CreateOptions): CreateProject; type PreviewOptions = { cwd: string; command?: string; }; +/** @deprecated Internal helper used by `prepareServer` - will be removed from public API in a future version. */ declare function startPreview({ cwd, command }: PreviewOptions): Promise<{ url: string; close: () => Promise; diff --git a/packages/sv/api-surface.md b/packages/sv/api-surface.md index 3efb6b89c..7a98dcb58 100644 --- a/packages/sv/api-surface.md +++ b/packages/sv/api-surface.md @@ -8,11 +8,13 @@ type LanguageType = (typeof languageTypes)[number]; declare const templateTypes: readonly ['minimal', 'demo', 'library', 'addon', 'svelte']; declare const languageTypes: readonly ['typescript', 'checkjs', 'none']; type Options = { + cwd: string; name: string; template: TemplateType; types: LanguageType; }; -declare function create(cwd: string, options: Options): void; +declare function create(cwd: string, options: Omit): void; +declare function create(options: Options): void; type FileEditor = Workspace & { content: string; }; @@ -22,48 +24,40 @@ type FileType = { content: (editor: FileEditor) => string; }; export { - Addon, - AddonDefinition, - AddonInput, + type Addon, + type AddonDefinition, + type AddonInput, type AddonMap, - AddonReference, - AddonResult, - AddonSource, - BaseQuestion, - BooleanQuestion, - ConditionDefinition, - ConfiguredAddon, - FileEditor, - FileType, + type AddonReference, + type AddonResult, + type AddonSource, + type BaseQuestion, + type BooleanQuestion, + type ConfiguredAddon, + type FileEditor, + type FileType, type InstallOptions, type LanguageType, - LoadedAddon, - MultiSelectQuestion, - NumberQuestion, - OptionBuilder, - OptionDefinition, + type LoadedAddon, + type MultiSelectQuestion, + type NumberQuestion, + type OptionBuilder, + type OptionDefinition, type OptionMap, - OptionValues, - PackageDefinition, - PreparedAddon, - Question, - Scripts, - SelectQuestion, - SetupResult, - StringQuestion, - SvApi, + type OptionValues, + type PreparedAddon, + type Question, + type SelectQuestion, + type SetupResult, + type StringQuestion, + type SvApi, type TemplateType, - TestDefinition, - Tests, - Verification, - Workspace, - WorkspaceOptions, + type Workspace, + type WorkspaceOptions, add, create, - createWorkspace, defineAddon, defineAddonOptions, - getErrorHint, officialAddons }; ``` diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 756a08ba2..b710343b1 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -219,7 +219,7 @@ export default defineAddon({ const hasPrettier = Boolean(dependencyVersion('prettier')); if (hasPrettier) { sv.file( - file.prettierignore, + '.prettierignore', transforms.text(({ content, text }) => text.upsert(content, '/drizzle/')) ); } diff --git a/packages/sv/src/addons/eslint.ts b/packages/sv/src/addons/eslint.ts index 58b1d00a4..4d6197676 100644 --- a/packages/sv/src/addons/eslint.ts +++ b/packages/sv/src/addons/eslint.ts @@ -31,7 +31,7 @@ export default defineAddon({ ); sv.file( - file.eslintConfig, + 'eslint.config.js', transforms.script(({ ast, comments, js }) => { const eslintConfigs: Array = []; js.imports.addDefault(ast, { from: './svelte.config.js', as: 'svelteConfig' }); @@ -156,14 +156,14 @@ export default defineAddon({ ); sv.file( - file.vscodeExtensions, + '.vscode/extensions.json', transforms.json(({ data, json }) => { json.arrayUpsert(data, 'recommendations', 'dbaeumer.vscode-eslint'); }) ); if (prettierInstalled) { - sv.file(file.eslintConfig, addEslintConfigPrettier); + sv.file('eslint.config.js', addEslintConfigPrettier); } } }); diff --git a/packages/sv/src/addons/prettier.ts b/packages/sv/src/addons/prettier.ts index eea8f1252..69629f2cc 100644 --- a/packages/sv/src/addons/prettier.ts +++ b/packages/sv/src/addons/prettier.ts @@ -16,7 +16,7 @@ export default defineAddon({ sv.devDependency('prettier-plugin-svelte', '^3.4.1'); sv.file( - file.prettierignore, + '.prettierignore', transforms.text(({ content }) => { if (content) return false; return dedent` @@ -34,7 +34,7 @@ export default defineAddon({ ); sv.file( - file.prettierrc, + '.prettierrc', transforms.json( ({ data, json }) => { if (Object.keys(data).length === 0) { @@ -82,7 +82,7 @@ export default defineAddon({ ); sv.file( - file.vscodeExtensions, + '.vscode/extensions.json', transforms.json(({ data, json }) => { json.arrayUpsert(data, 'recommendations', 'esbenp.prettier-vscode'); }) @@ -98,7 +98,7 @@ export default defineAddon({ if (eslintInstalled) { sv.devDependency('eslint-config-prettier', '^10.1.8'); - sv.file(file.eslintConfig, addEslintConfigPrettier); + sv.file('eslint.config.js', addEslintConfigPrettier); } } }); diff --git a/packages/sv/src/addons/sveltekit-adapter.ts b/packages/sv/src/addons/sveltekit-adapter.ts index dd3f9a3a2..4f2340da7 100644 --- a/packages/sv/src/addons/sveltekit-adapter.ts +++ b/packages/sv/src/addons/sveltekit-adapter.ts @@ -1,10 +1,10 @@ import { color, - resolveCommandArray, text, transforms, + resolveCommandArray, fileExists, - getPackageJson, + loadPackageJson, sanitizeName } from '@sveltejs/sv-utils'; import { defineAddon, defineAddonOptions } from '../core/config.ts'; @@ -140,7 +140,7 @@ export default defineAddon({ } if (!data.name) { - const pkg = getPackageJson(cwd); + const pkg = loadPackageJson(cwd); data.name = sanitizeName(pkg.data.name, 'wrangler'); } diff --git a/packages/sv/src/addons/tailwindcss.ts b/packages/sv/src/addons/tailwindcss.ts index 151d0fe66..2755ea392 100644 --- a/packages/sv/src/addons/tailwindcss.ts +++ b/packages/sv/src/addons/tailwindcss.ts @@ -108,7 +108,7 @@ export default defineAddon({ } sv.file( - file.vscodeSettings, + '.vscode/settings.json', transforms.json(({ data }) => { data['files.associations'] ??= {}; data['files.associations']['*.css'] = 'tailwindcss'; @@ -116,7 +116,7 @@ export default defineAddon({ ); sv.file( - file.vscodeExtensions, + '.vscode/extensions.json', transforms.json(({ data, json }) => { json.arrayUpsert(data, 'recommendations', 'bradlc.vscode-tailwindcss'); }) @@ -124,7 +124,7 @@ export default defineAddon({ if (prettierInstalled) { sv.file( - file.prettierrc, + '.prettierrc', transforms.json(({ data, json }) => { json.arrayUpsert(data, 'plugins', 'prettier-plugin-tailwindcss'); data.tailwindStylesheet ??= file.getRelative({ to: file.stylesheet }); diff --git a/packages/sv/src/cli/create.ts b/packages/sv/src/cli/create.ts index a2cf4d388..f8793efc2 100644 --- a/packages/sv/src/cli/create.ts +++ b/packages/sv/src/cli/create.ts @@ -1,5 +1,5 @@ import * as p from '@clack/prompts'; -import { color, resolveCommandArray, commonFilePaths, getPackageJson } from '@sveltejs/sv-utils'; +import { color, commonFilePaths, loadPackageJson, resolveCommandArray } from '@sveltejs/sv-utils'; import { Command, Option } from 'commander'; import fs from 'node:fs'; import path from 'node:path'; @@ -313,7 +313,8 @@ async function createProject(cwd: ProjectPath, options: Options) { answers = result.answers; } - createKit(projectPath, { + createKit({ + cwd: projectPath, name: projectName, template, types: language @@ -453,7 +454,7 @@ export async function createVirtualWorkspace({ // Let's read the package.json of the template we will use and add the dependencies to the override const templatePackageJsonPath = dist(`templates/${template}`); - const { data: packageJson } = getPackageJson(templatePackageJsonPath); + const { data: packageJson } = loadPackageJson(templatePackageJsonPath); override.dependencies = { ...packageJson.devDependencies, ...packageJson.dependencies, diff --git a/packages/sv/src/core/common.ts b/packages/sv/src/core/common.ts index 856b8e0c5..aace74cc8 100644 --- a/packages/sv/src/core/common.ts +++ b/packages/sv/src/core/common.ts @@ -1,9 +1,9 @@ import * as p from '@clack/prompts'; import { - type AgentName, color, - resolveCommandArray, - isVersionUnsupportedBelow + isVersionUnsupportedBelow, + type AgentName, + resolveCommandArray } from '@sveltejs/sv-utils'; import type { Argument, Command, Help, HelpConfiguration, Option } from 'commander'; import fs from 'node:fs'; diff --git a/packages/sv/src/core/engine.ts b/packages/sv/src/core/engine.ts index 8a8be6df1..c9bdeb1ff 100644 --- a/packages/sv/src/core/engine.ts +++ b/packages/sv/src/core/engine.ts @@ -1,12 +1,13 @@ import * as p from '@clack/prompts'; import { color, - resolveCommand, - type AgentName, + commonFilePaths, fileExists, - installPackages, - readFile, - writeFile + loadFile, + loadPackageJson, + saveFile, + resolveCommand, + type AgentName } from '@sveltejs/sv-utils'; import { NonZeroExitError, exec } from 'tinyexec'; import { createLoadedAddon } from '../cli/add.ts'; @@ -22,6 +23,38 @@ import { import { TESTING } from './env.ts'; import { createWorkspace, type Workspace } from './workspace.ts'; +function alphabetizePackageJsonDependencies(obj: Record) { + const ordered: Record = {}; + for (const [key, value] of Object.entries(obj).sort(([a], [b]) => a.localeCompare(b))) { + ordered[key] = value; + } + return ordered; +} + +function updatePackages( + dependencies: Array<{ pkg: string; version: string; dev: boolean }>, + cwd: string +): string { + const { data, generateCode } = loadPackageJson(cwd); + + for (const dependency of dependencies) { + if (dependency.dev) { + data.devDependencies ??= {}; + data.devDependencies[dependency.pkg] = dependency.version; + } else { + data.dependencies ??= {}; + data.dependencies[dependency.pkg] = dependency.version; + } + } + + if (data.dependencies) data.dependencies = alphabetizePackageJsonDependencies(data.dependencies); + if (data.devDependencies) + data.devDependencies = alphabetizePackageJsonDependencies(data.devDependencies); + + saveFile(cwd, commonFilePaths.packageJson, generateCode()); + return commonFilePaths.packageJson; +} + export type InstallOptions = { cwd: string; addons: Addons; @@ -175,11 +208,11 @@ async function runAddon({ addon, loaded, multiple, workspace, workspaceOptions } const sv: SvApi = { file: (path, edit) => { try { - const content = fileExists(workspace.cwd, path) ? readFile(workspace.cwd, path) : ''; + const content = fileExists(workspace.cwd, path) ? loadFile(workspace.cwd, path) : ''; const editedContent = edit(content); if (editedContent === '' || editedContent === false) return content; - writeFile(workspace.cwd, path, editedContent); + saveFile(workspace.cwd, path, editedContent); files.add(path); } catch (e) { if (e instanceof Error) { @@ -242,7 +275,7 @@ async function runAddon({ addon, loaded, multiple, workspace, workspaceOptions } } if (cancels.length === 0) { - const pkgPath = installPackages(dependencies, workspace.cwd); + const pkgPath = updatePackages(dependencies, workspace.cwd); files.add(pkgPath); } diff --git a/packages/sv/src/core/workspace.ts b/packages/sv/src/core/workspace.ts index 092d75627..cb38bcb9b 100644 --- a/packages/sv/src/core/workspace.ts +++ b/packages/sv/src/core/workspace.ts @@ -4,8 +4,8 @@ import { js, parse, commonFilePaths, - getPackageJson, - readFile + loadFile, + loadPackageJson } from '@sveltejs/sv-utils'; import * as find from 'empathic/find'; import fs from 'node:fs'; @@ -37,13 +37,6 @@ export type Workspace = { package: 'package.json'; gitignore: '.gitignore'; - prettierignore: '.prettierignore'; - prettierrc: '.prettierrc'; - eslintConfig: 'eslint.config.js'; - - vscodeSettings: '.vscode/settings.json'; - vscodeExtensions: '.vscode/extensions.json'; - /** Get the relative path between two files */ getRelative: ({ from, to }: { from?: string; to: string }) => string; @@ -109,7 +102,7 @@ export async function createWorkspace({ directory.length >= workspaceRoot.length ) { if (fs.existsSync(path.join(directory, commonFilePaths.packageJson))) { - const { data: packageJson } = getPackageJson(directory); + const { data: packageJson } = loadPackageJson(directory); dependencies = { ...packageJson.devDependencies, ...packageJson.dependencies, @@ -148,11 +141,6 @@ export async function createWorkspace({ stylesheet, package: 'package.json', gitignore: '.gitignore', - prettierignore: '.prettierignore', - prettierrc: '.prettierrc', - eslintConfig: 'eslint.config.js', - vscodeSettings: '.vscode/settings.json', - vscodeExtensions: '.vscode/extensions.json', getRelative({ from, to }) { from = from ?? ''; let relativePath = path.posix.relative(path.posix.dirname(from), to); @@ -188,7 +176,7 @@ function findWorkspaceRoot(cwd: string): string { return directory; } // in other package managers it's a workspaces key in the package.json - const { data } = getPackageJson(directory); + const { data } = loadPackageJson(directory); if (data.workspaces) { return directory; } @@ -204,7 +192,7 @@ function findWorkspaceRoot(cwd: string): string { } function parseKitOptions(cwd: string, svelteConfigPath: string) { - const configSource = readFile(cwd, svelteConfigPath); + const configSource = loadFile(cwd, svelteConfigPath); const { ast } = parse.script(configSource); const defaultExport = ast.body.find((s) => s.type === 'ExportDefaultDeclaration'); diff --git a/packages/sv/src/create/index.ts b/packages/sv/src/create/index.ts index 5a2947cbe..5b78b6c45 100644 --- a/packages/sv/src/create/index.ts +++ b/packages/sv/src/create/index.ts @@ -11,6 +11,7 @@ const templateTypes = ['minimal', 'demo', 'library', 'addon', 'svelte'] as const const languageTypes = ['typescript', 'checkjs', 'none'] as const; export type Options = { + cwd: string; name: string; template: TemplateType; types: LanguageType; @@ -32,7 +33,19 @@ export type Common = { }>; }; -export function create(cwd: string, options: Options): void { +export function create(cwd: string, options: Omit): void; +export function create(options: Options): void; +export function create(cwdOrOptions: string | Options, legacyOptions?: Omit): void { + let cwd: string; + let options: Omit; + if (typeof cwdOrOptions === 'string') { + cwd = cwdOrOptions; + options = legacyOptions!; + } else { + cwd = cwdOrOptions.cwd; + options = cwdOrOptions; + } + mkdirp(cwd); write_template_files(options.template, options.types, options.name, cwd); @@ -76,7 +89,7 @@ function write_template_files(template: string, types: LanguageType, name: strin }); } -function write_common_files(cwd: string, options: Options, name: string) { +function write_common_files(cwd: string, options: Omit, name: string) { const files = getSharedFiles(); const pkg_file = path.join(cwd, commonFilePaths.packageJson); @@ -105,7 +118,7 @@ function write_common_files(cwd: string, options: Options, name: string) { fs.writeFileSync(pkg_file, JSON.stringify(pkg, null, '\t') + '\n'); } -function matches_condition(condition: Condition, options: Options) { +function matches_condition(condition: Condition, options: Omit) { if (templateTypes.includes(condition as TemplateType)) { return options.template === condition; } diff --git a/packages/sv/src/create/tests/check.ts b/packages/sv/src/create/tests/check.ts index e00b3c33f..c214a375b 100644 --- a/packages/sv/src/create/tests/check.ts +++ b/packages/sv/src/create/tests/check.ts @@ -42,7 +42,7 @@ for (const template of templates.filter((t) => t !== 'addon')) { const cwd = path.join(test_workspace_dir, `${template}-${types}`); fs.rmSync(cwd, { recursive: true, force: true }); - create(cwd, { name: `create-svelte-test-${template}-${types}`, template, types }); + create({ cwd, name: `create-svelte-test-${template}-${types}`, template, types }); await add({ cwd, addons: { eslint: officialAddons.eslint }, options: { eslint: {} } }); const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')); diff --git a/packages/sv/src/create/tests/playground.ts b/packages/sv/src/create/tests/playground.ts index fbc79b5d1..5d7778de0 100644 --- a/packages/sv/src/create/tests/playground.ts +++ b/packages/sv/src/create/tests/playground.ts @@ -153,7 +153,8 @@ test('real world download and convert playground async', async () => { fs.rmSync(directory, { recursive: true }); } - create(directory, { + create({ + cwd: directory, name: 'real-world-playground', template: 'minimal', types: 'typescript' @@ -204,7 +205,8 @@ test('real world download and convert playground without async', async () => { fs.rmSync(directory, { recursive: true }); } - create(directory, { + create({ + cwd: directory, name: 'real-world-playground-old', template: 'minimal', types: 'typescript' diff --git a/packages/sv/src/index.ts b/packages/sv/src/index.ts index 874abb260..3c3fe50b0 100644 --- a/packages/sv/src/index.ts +++ b/packages/sv/src/index.ts @@ -4,7 +4,37 @@ export type { AddonMap, InstallOptions, OptionMap } from './core/engine.ts'; export { officialAddons } from './addons/index.ts'; // Addon authoring API export { defineAddon, defineAddonOptions } from './core/config.ts'; -export type * from './core/processors.ts'; -export type * from './core/options.ts'; -export type * from './core/config.ts'; -export type * from './core/workspace.ts'; + +// options.ts - question types for addon options +export type { + Question, + OptionDefinition, + OptionValues, + BooleanQuestion, + StringQuestion, + NumberQuestion, + SelectQuestion, + MultiSelectQuestion, + BaseQuestion +} from './core/options.ts'; + +// config.ts - addon definition and pipeline types +export type { + Addon, + SvApi, + AddonDefinition, + SetupResult, + OptionBuilder, + AddonInput, + AddonSource, + AddonReference, + LoadedAddon, + PreparedAddon, + ConfiguredAddon, + AddonResult +} from './core/config.ts'; + +// workspace.ts +export type { Workspace, WorkspaceOptions } from './core/workspace.ts'; + +export type { FileEditor, FileType } from './core/processors.ts'; diff --git a/packages/sv/src/testing.ts b/packages/sv/src/testing.ts index 35d624cce..02d6c14b2 100644 --- a/packages/sv/src/testing.ts +++ b/packages/sv/src/testing.ts @@ -28,6 +28,7 @@ type SetupOptions = { /** @default false */ clean?: boolean; }; +/** @deprecated Internal helper used by `createSetupTest` - will be removed from public API in a future version. */ export function setup({ cwd, clean = false, variants }: SetupOptions): { templatesDir: string } { const workingDir = path.resolve(cwd); if (clean && fs.existsSync(workingDir)) { @@ -42,13 +43,13 @@ export function setup({ cwd, clean = false, variants }: SetupOptions): { templat if (fs.existsSync(templatePath)) continue; if (variant === 'kit-js') { - create(templatePath, { name: variant, template: 'minimal', types: 'checkjs' }); + create({ cwd: templatePath, name: variant, template: 'minimal', types: 'checkjs' }); } else if (variant === 'kit-ts') { - create(templatePath, { name: variant, template: 'minimal', types: 'typescript' }); + create({ cwd: templatePath, name: variant, template: 'minimal', types: 'typescript' }); } else if (variant === 'vite-js') { - create(templatePath, { name: variant, template: 'svelte', types: 'none' }); + create({ cwd: templatePath, name: variant, template: 'svelte', types: 'none' }); } else if (variant === 'vite-ts') { - create(templatePath, { name: variant, template: 'svelte', types: 'typescript' }); + create({ cwd: templatePath, name: variant, template: 'svelte', types: 'typescript' }); } else { throw new Error(`Unknown project variant: ${variant}`); } @@ -58,6 +59,7 @@ export function setup({ cwd, clean = false, variants }: SetupOptions): { templat } type CreateOptions = { cwd: string; testName: string; templatesDir: string }; +/** @deprecated Internal helper used by `createSetupTest` - will be removed from public API in a future version. */ export function createProject({ cwd, testName, templatesDir }: CreateOptions): CreateProject { // create the reference dir const testDir = path.resolve(cwd, testName); @@ -74,6 +76,7 @@ export function createProject({ cwd, testName, templatesDir }: CreateOptions): C } type PreviewOptions = { cwd: string; command?: string }; +/** @deprecated Internal helper used by `prepareServer` - will be removed from public API in a future version. */ export async function startPreview({ cwd, command = 'npm run preview' diff --git a/tsdown.config.ts b/tsdown.config.ts index 7de467cb0..ce4017a73 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -124,8 +124,15 @@ export default defineConfig([ }, failOnWarn: true, deps: { - neverBundle: [/^svelte/, '@types/estree', 'estree'], - onlyBundle: ['dedent', 'package-manager-detector', 'smol-toml', 'yaml', 'zimmerframe'] + neverBundle: [ + /^svelte/, + '@types/estree', + 'estree', + 'yaml', + 'dedent', + 'package-manager-detector' + ], + onlyBundle: ['smol-toml', 'zimmerframe'] }, hooks: { 'build:done': () => hookApiSurfaceBuildDone()