diff --git a/.changeset/stupid-trains-smile.md b/.changeset/stupid-trains-smile.md new file mode 100644 index 000000000..e88ba261a --- /dev/null +++ b/.changeset/stupid-trains-smile.md @@ -0,0 +1,5 @@ +--- +"shadcn-svelte": patch +--- + +Preserve the content of `svelte.config.js` when running the `init` command diff --git a/packages/cli/src/astravel/attachComments.js b/packages/cli/src/astravel/attachComments.js new file mode 100644 index 000000000..8032dd049 --- /dev/null +++ b/packages/cli/src/astravel/attachComments.js @@ -0,0 +1,118 @@ +import { defaultTraveler } from './defaultTraveler' + +function attachCommentsToNode( + traveler, + state, + parent, + children, + findHeadingComments, +) { + let { index } = state + const { comments } = state + let comment = comments[index] + // Hack to tackle https://github.com/babel/minify/issues/866 + let boundComments, trailingComments + if (comment == null) { + return + } + if (children == null || children.length === 0) { + // No children, attach comments to parent + boundComments = parent.comments != null ? parent.comments : [] + while (comment != null && comment.end <= parent.end) { + boundComments.push(comment) + comment = comments[++index] + } + state.index = index + if (boundComments.length !== 0 && parent.comments == null) { + parent.comments = boundComments + } + return + } + // Look for heading block comments not immediately followed by a child + if (findHeadingComments) { + boundComments = parent.comments != null ? parent.comments : [] + const { start } = children[0] + while ( + comment != null && + (comment.type[0] === 'B' || comment.type[0] === 'M') && + comment.end <= start + ) { + boundComments.push(comment) + comment = comments[++index] + } + if (boundComments.length !== 0 && parent.comments == null) + parent.comments = boundComments + } + // Attach comments to children + for (let i = 0, { length } = children; comment != null && i < length; i++) { + const child = children[i] + boundComments = [] + while (comment != null && comment.end <= child.start) { + boundComments.push(comment) + comment = comments[++index] + } + // Check if next comment is line comment and on the same line if location is provided + if ( + comment != null && + comment.loc != null && + (comment.type[0] === 'L' || comment.type[0] === 'S') + ) { + if (comment.loc.start.line === child.loc.end.line) { + boundComments.push(comment) + comment = comments[++index] + } + } + if (boundComments.length !== 0) { + child.comments = boundComments + } + // Travel through child + state.index = index + traveler[child.type](child, state) + index = state.index + comment = comments[index] + } + // Look for remaining comments + trailingComments = [] + while (comment != null && comment.end <= parent.end) { + trailingComments.push(comment) + comment = comments[++index] + } + if (trailingComments.length !== 0) { + parent.trailingComments = trailingComments + } + state.index = index +} + +function Block(node, state) { + attachCommentsToNode(this, state, node, node.body, true) +} + +let traveler = defaultTraveler.makeChild({ + Program: Block, + BlockStatement: Block, + ClassBody: Block, + ObjectExpression(node, state) { + attachCommentsToNode(this, state, node, node.properties, true) + }, + ArrayExpression(node, state) { + attachCommentsToNode(this, state, node, node.elements, true) + }, + SwitchStatement(node, state) { + attachCommentsToNode(this, state, node, node.cases, false) + }, + SwitchCase(node, state) { + attachCommentsToNode(this, state, node, node.consequent, false) + }, + // TODO: Consider ArrayExpression ? +}) + +export function attachComments(node, comments) { + /* + Modifies in-place the AST starting at `node` by attaching the provided `comments` and returns that AST. + */ + traveler[node.type](node, { + comments, + index: 0, + }) + return node +} diff --git a/packages/cli/src/astravel/defaultTraveler.js b/packages/cli/src/astravel/defaultTraveler.js new file mode 100644 index 000000000..2d30ba2b8 --- /dev/null +++ b/packages/cli/src/astravel/defaultTraveler.js @@ -0,0 +1,376 @@ +let ForInStatement, + FunctionDeclaration, + RestElement, + BinaryExpression, + ArrayExpression, + Block, + MethodDefinition + +const ignore = Function.prototype + +class Found { + constructor(node, state) { + this.node = node + this.state = state + } +} + +export const defaultTraveler = { + go(node, state) { + /* + Starts travelling through the specified AST `node` with the provided `state`. + This method is recursively called by each node handler. + */ + if (this[node.type]) { + this[node.type](node, state) + } + }, + find(predicate, node, state) { + /* + Returns { node, state } for which `predicate(node, state)` returns truthy, + starting at the specified AST `node` and with the provided `state`. + Otherwise, returns `undefined`. + */ + const finder = Object.create(this) + finder.go = function (node, state) { + if (predicate(node, state)) { + throw new Found(node, state) + } + this[node.type](node, state) + } + try { + finder.go(node, state) + } catch (error) { + if (error instanceof Found) { + return error + } else { + throw error + } + } + }, + makeChild(properties = {}) { + /* + Returns a custom AST traveler that inherits from `this` traveler with its own provided `properties` and the property `super` that points to the parent traveler object. + */ + const traveler = Object.create(this) + traveler.super = this + for (let key in properties) { + traveler[key] = properties[key] + } + return traveler + }, + Program: (Block = function (node, state) { + const { body } = node + if (body != null) { + const { length } = body + for (let i = 0; i < length; i++) { + this.go(body[i], state) + } + } + }), + BlockStatement: Block, + StaticBlock: Block, + EmptyStatement: ignore, + ExpressionStatement(node, state) { + this.go(node.expression, state) + }, + IfStatement(node, state) { + this.go(node.test, state) + this.go(node.consequent, state) + if (node.alternate != null) { + this.go(node.alternate, state) + } + }, + LabeledStatement(node, state) { + this.go(node.label, state) + this.go(node.body, state) + }, + BreakStatement(node, state) { + if (node.label) { + this.go(node.label, state) + } + }, + ContinueStatement(node, state) { + if (node.label) { + this.go(node.label, state) + } + }, + WithStatement(node, state) { + this.go(node.object, state) + this.go(node.body, state) + }, + SwitchStatement(node, state) { + this.go(node.discriminant, state) + const { cases } = node, + { length } = cases + for (let i = 0; i < length; i++) { + this.go(cases[i], state) + } + }, + SwitchCase(node, state) { + if (node.test != null) { + this.go(node.test, state) + } + const statements = node.consequent, + { length } = statements + for (let i = 0; i < length; i++) { + this.go(statements[i], state) + } + }, + ReturnStatement(node, state) { + if (node.argument) { + this.go(node.argument, state) + } + }, + ThrowStatement(node, state) { + this.go(node.argument, state) + }, + TryStatement(node, state) { + this.go(node.block, state) + if (node.handler != null) { + this.go(node.handler, state) + } + if (node.finalizer != null) { + this.go(node.finalizer, state) + } + }, + CatchClause(node, state) { + if (node.param != null) { + this.go(node.param, state) + } + this.go(node.body, state) + }, + WhileStatement(node, state) { + this.go(node.test, state) + this.go(node.body, state) + }, + DoWhileStatement(node, state) { + this.go(node.body, state) + this.go(node.test, state) + }, + ForStatement(node, state) { + if (node.init != null) { + this.go(node.init, state) + } + if (node.test != null) { + this.go(node.test, state) + } + if (node.update != null) { + this.go(node.update, state) + } + this.go(node.body, state) + }, + ForInStatement: (ForInStatement = function (node, state) { + this.go(node.left, state) + this.go(node.right, state) + this.go(node.body, state) + }), + DebuggerStatement: ignore, + FunctionDeclaration: (FunctionDeclaration = function (node, state) { + if (node.id != null) { + this.go(node.id, state) + } + const { params } = node + if (params != null) { + for (let i = 0, { length } = params; i < length; i++) { + this.go(params[i], state) + } + } + this.go(node.body, state) + }), + VariableDeclaration(node, state) { + const { declarations } = node, + { length } = declarations + for (let i = 0; i < length; i++) { + this.go(declarations[i], state) + } + }, + VariableDeclarator(node, state) { + this.go(node.id, state) + if (node.init != null) { + this.go(node.init, state) + } + }, + ArrowFunctionExpression(node, state) { + const { params } = node + if (params != null) { + for (let i = 0, { length } = params; i < length; i++) { + this.go(params[i], state) + } + } + this.go(node.body, state) + }, + ThisExpression: ignore, + ArrayExpression: (ArrayExpression = function (node, state) { + const { elements } = node, + { length } = elements + for (let i = 0; i < length; i++) { + let element = elements[i] + if (element != null) { + this.go(elements[i], state) + } + } + }), + ObjectExpression(node, state) { + const { properties } = node, + { length } = properties + for (let i = 0; i < length; i++) { + this.go(properties[i], state) + } + }, + Property(node, state) { + this.go(node.key, state) + if (node.value != null) { + this.go(node.value, state) + } + }, + FunctionExpression: FunctionDeclaration, + SequenceExpression(node, state) { + const { expressions } = node, + { length } = expressions + for (let i = 0; i < length; i++) { + this.go(expressions[i], state) + } + }, + UnaryExpression(node, state) { + this.go(node.argument, state) + }, + UpdateExpression(node, state) { + this.go(node.argument, state) + }, + AssignmentExpression(node, state) { + this.go(node.left, state) + this.go(node.right, state) + }, + BinaryExpression: (BinaryExpression = function (node, state) { + this.go(node.left, state) + this.go(node.right, state) + }), + LogicalExpression: BinaryExpression, + ConditionalExpression(node, state) { + this.go(node.test, state) + this.go(node.consequent, state) + this.go(node.alternate, state) + }, + NewExpression(node, state) { + this.CallExpression(node, state) + }, + CallExpression(node, state) { + this.go(node.callee, state) + const args = node['arguments'], + { length } = args + for (let i = 0; i < length; i++) { + this.go(args[i], state) + } + }, + MemberExpression(node, state) { + this.go(node.object, state) + this.go(node.property, state) + }, + Identifier: ignore, + PrivateIdentifier: ignore, + Literal: ignore, + ForOfStatement: ForInStatement, + ClassDeclaration(node, state) { + if (node.id) { + this.go(node.id, state) + } + if (node.superClass) { + this.go(node.superClass, state) + } + this.go(node.body, state) + }, + ClassBody: Block, + ImportDeclaration(node, state) { + const { specifiers } = node, + { length } = specifiers + for (let i = 0; i < length; i++) { + this.go(specifiers[i], state) + } + this.go(node.source, state) + }, + ImportNamespaceSpecifier(node, state) { + this.go(node.local, state) + }, + ImportDefaultSpecifier(node, state) { + this.go(node.local, state) + }, + ImportSpecifier(node, state) { + this.go(node.imported, state) + this.go(node.local, state) + }, + ExportDefaultDeclaration(node, state) { + this.go(node.declaration, state) + }, + ExportNamedDeclaration(node, state) { + if (node.declaration) { + this.go(node.declaration, state) + } + const { specifiers } = node, + { length } = specifiers + for (let i = 0; i < length; i++) { + this.go(specifiers[i], state) + } + if (node.source) { + this.go(node.source, state) + } + }, + ExportSpecifier(node, state) { + this.go(node.local, state) + this.go(node.exported, state) + }, + ExportAllDeclaration(node, state) { + this.go(node.source, state) + }, + MethodDefinition: (MethodDefinition = function (node, state) { + this.go(node.key, state) + this.go(node.value, state) + }), + PropertyDefinition: MethodDefinition, + ClassExpression(node, state) { + this.ClassDeclaration(node, state) + }, + Super: ignore, + RestElement: (RestElement = function (node, state) { + this.go(node.argument, state) + }), + SpreadElement: RestElement, + YieldExpression(node, state) { + if (node.argument) { + this.go(node.argument, state) + } + }, + TaggedTemplateExpression(node, state) { + this.go(node.tag, state) + this.go(node.quasi, state) + }, + TemplateLiteral(node, state) { + const { quasis, expressions } = node + for (let i = 0, { length } = expressions; i < length; i++) { + this.go(expressions[i], state) + } + for (let i = 0, { length } = quasis; i < length; i++) { + this.go(quasis[i], state) + } + }, + TemplateElement: ignore, + ObjectPattern(node, state) { + const { properties } = node, + { length } = properties + for (let i = 0; i < length; i++) { + this.go(properties[i], state) + } + }, + ArrayPattern: ArrayExpression, + AssignmentPattern(node, state) { + this.go(node.left, state) + this.go(node.right, state) + }, + MetaProperty(node, state) { + this.go(node.meta, state) + this.go(node.property, state) + }, + AwaitExpression(node, state) { + this.go(node.argument, state) + }, +} diff --git a/packages/cli/src/astravel/index.js b/packages/cli/src/astravel/index.js new file mode 100644 index 000000000..57b7864a7 --- /dev/null +++ b/packages/cli/src/astravel/index.js @@ -0,0 +1,21 @@ +// Astravel is tiny and fast ESTree-compliant AST walker and modifier. +// +// Astravel was written by David Bonnet and released under an MIT license. +// +// The Git repository for Astravel is available at: +// https://github.com/davidbonnet/astravel.git +// +// Please use the GitHub bug tracker to report issues: +// https://github.com/davidbonnet/astravel/issues + +import { defaultTraveler } from './defaultTraveler' +export { attachComments } from './attachComments' + +export function makeTraveler(properties) { + /* + Returns a custom AST traveler that inherits from the `defaultTraveler` with its own provided `properties` and the property `super` that points to the parent traveler object. + */ + return defaultTraveler.makeChild(properties) +} + +export { defaultTraveler } diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 1ecd168a8..36e93986b 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -11,7 +11,7 @@ import { getPackageInfo } from "./utils/get-package-info"; import { getPackageManager } from "./utils/get-package-manager"; import { getProjectInfo } from "./utils/get-project-info"; import { logger } from "./utils/logger"; -import { getConfig, setConfig } from "./utils/set-config"; +import { addAliases, getConfig, setConfig } from "./utils/set-config"; import { STYLES, TAILWIND_CONFIG, UTILS } from "./utils/templates"; process.on("SIGINT", () => process.exit(0)); @@ -107,15 +107,11 @@ async function main() { tailwindSpinner.succeed(); // Update svelte.config.js - const svelteConfigDestination = "./svelte.config.js"; const svelteConfigSpinner = ora( `Updating svelte.config.js...` ).start(); - await fs.writeFile( - svelteConfigDestination, - TAILWIND_CONFIG, - "utf8" - ); + + await addAliases(); svelteConfigSpinner.succeed(); }); @@ -218,7 +214,7 @@ async function promptForComponents(components: Component[]) { } async function promptForDestinationDir() { - const config = getConfig(); + const config = await getConfig(); if (!config) { const { dir } = await prompts([ @@ -229,7 +225,7 @@ async function promptForDestinationDir() { initial: "./src/lib/components/ui" } ]); - setConfig(dir); + await setConfig(dir); return dir; } diff --git a/packages/cli/src/utils/schemas.ts b/packages/cli/src/utils/schemas.ts index 7c4213a70..d01ce4d20 100644 --- a/packages/cli/src/utils/schemas.ts +++ b/packages/cli/src/utils/schemas.ts @@ -1,28 +1,7 @@ import { z } from "zod"; -export const shadConfigSchema = z - .object({ - value: z.object({ - type: z.literal("ObjectExpression"), - properties: z.array( - z.object({ - type: z.literal("Property"), - key: z.object({ - type: z.literal("Identifier"), - name: z.enum(["componentPath"]) - }), - value: z.object({ - type: z.literal("Literal"), - value: z.string() - }) - }) - ) - }) - }) - .transform((data) => { - return { - componentPath: data.value.properties[0].value.value - }; - }); +export const shadConfigSchema = z.object({ + componentPath: z.string() +}); export type ShadConfig = z.infer; diff --git a/packages/cli/src/utils/set-config.ts b/packages/cli/src/utils/set-config.ts index 69d03a177..f00700215 100644 --- a/packages/cli/src/utils/set-config.ts +++ b/packages/cli/src/utils/set-config.ts @@ -2,25 +2,42 @@ import fs from "fs"; import path from "path"; import type { Property } from "estree"; import type { Node } from "estree-walker"; -import { parse } from "acorn"; +import { Comment, parse } from "acorn"; import { generate } from "astring"; import { walk } from "estree-walker"; import prettier from "prettier"; +// @ts-expect-error no types +// had to manually add this package since their package.json is misconfigured +import { attachComments } from "../astravel"; import { ShadConfig, shadConfigSchema } from "./schemas"; -export function getConfig(): ShadConfig | null { - const svelteConfigPath = path.join(process.cwd(), "svelte.config.js"); +const SVELTE_CONFIG_PATH = path.join(process.cwd(), "svelte.config.js"); - const svelteConfig = fs.readFileSync(svelteConfigPath, "utf8"); +export async function getConfig(): Promise { + const { default: svelteConfig } = await import(SVELTE_CONFIG_PATH); - const ast = parse(svelteConfig, { - ecmaVersion: "latest", - sourceType: "module" - }); + try { + const shadConfig = shadConfigSchema.parse(svelteConfig.shadcn); + return shadConfig; + } catch (e) { + return null; + } +} - let shadConfigNode: Node | null = null; +/** + * Parse the svelte.config.js file into an abstract syntax tree (AST), + * then walk the tree to find the config object and add the shadcn + * property to it with the componentPath property. + */ +export async function setConfig(dir: string = "./src/lib/components/ui") { + const { default: config } = await import(SVELTE_CONFIG_PATH); + if (!config) { + throw new Error("Could not find config for svelte.config.js"); + } - walk(ast as Node, { + const { ast, comments } = await parseSvelteConfig(); + + const updatedSvelteConfig = walk(ast as Node, { enter(node) { if ( node.type === "VariableDeclaration" && @@ -34,70 +51,60 @@ export function getConfig(): ShadConfig | null { configNode.init && configNode.init.type === "ObjectExpression" ) { - const shadConfig = configNode.init.properties.find( - (property) => { - if (property.type !== "Property") { - return false; - } - if (property.key.type !== "Identifier") { - return false; - } - return property.key.name === "shadcn"; - } - ); - - if (!shadConfig || shadConfig.type !== "Property") { - return; - } - - shadConfigNode = shadConfig; - return; + configNode.init.properties.push(createConfigNode(dir)); } } } }); - if (!shadConfigNode) { - return null; - } - - try { - const shadConfig = shadConfigSchema.parse(shadConfigNode); - return shadConfig; - } catch (e) { - return null; + if (!updatedSvelteConfig) { + throw new Error("Could not update svelte.config.js"); } -} -export function setConfig(dir: string = "./src/lib/components/ui") { - // Parse the svelte.config.js file into an abstract syntax tree (AST), - // then walk the tree to find the config object and add the shadcn - // property to it with the componentPath property. - - const svelteConfigPath = path.join(process.cwd(), "svelte.config.js"); + attachComments(updatedSvelteConfig, comments); + const updatedSvelteConfigString = generate(updatedSvelteConfig, { + comments: true + }); - const svelteConfig = fs.readFileSync(svelteConfigPath, "utf8"); + return formatFile(updatedSvelteConfigString, SVELTE_CONFIG_PATH); +} - const ast = parse(svelteConfig, { - ecmaVersion: "latest", - sourceType: "module" - }); +/** + * Parse the svelte.config.js file into an abstract syntax tree (AST), + * then walk the tree to find the config object and adds the alias + * property to it + */ +export async function addAliases(dir: string = "./src/lib/components/ui") { + const { ast, comments } = await parseSvelteConfig(); const updatedSvelteConfig = walk(ast as Node, { enter(node) { if ( - node.type === "VariableDeclaration" && - node.kind === "const" && - node.declarations[0].type === "VariableDeclarator" && - node.declarations[0].id.type === "Identifier" && - node.declarations[0].id.name === "config" + node.type === "Property" && + node.key.type === "Identifier" && + node.key.name === "kit" && + node.value.type === "ObjectExpression" ) { - const configNode = node.declarations[0]; + // check if `alias` is already defined + const aliasProp = node.value.properties.find( + (n) => + n.type === "Property" && + n.value.type === "ObjectExpression" && + n.key.type === "Identifier" && + n.key.name === "alias" + ) as Property | undefined; + + if (!aliasProp) { + // alias is not defined so we'll add it + node.value.properties.push(createAliasNode()); + return; + } + if ( - configNode.init && - configNode.init.type === "ObjectExpression" + aliasProp.type === "Property" && + aliasProp.value.type === "ObjectExpression" ) { - configNode.init.properties.push(createConfigNode(dir)); + aliasProp.value.properties.push(...createAliasProperties()); } } } @@ -107,27 +114,45 @@ export function setConfig(dir: string = "./src/lib/components/ui") { throw new Error("Could not update svelte.config.js"); } - const updatedSvelteConfigString = generate(updatedSvelteConfig); + attachComments(updatedSvelteConfig, comments); + const updatedSvelteConfigString = generate(updatedSvelteConfig, { + comments: true + }); + + return formatFile(updatedSvelteConfigString, SVELTE_CONFIG_PATH); +} + +async function parseSvelteConfig() { + const { default: config } = await import(SVELTE_CONFIG_PATH); + if (!config) { + throw new Error("Could not find config for svelte.config.js"); + } - const prettierConfigFile = - prettier.resolveConfigFile.sync(svelteConfigPath); + const svelteConfig = fs.readFileSync(SVELTE_CONFIG_PATH, "utf8"); + const comments: Comment[] = []; + const ast = parse(svelteConfig, { + ecmaVersion: "latest", + sourceType: "module", + onComment: comments + }); + + return { ast, comments }; +} + +function formatFile(content: string, path: string) { + const prettierConfigFile = prettier.resolveConfigFile.sync(path); if (!prettierConfigFile) { - return fs.writeFileSync(svelteConfigPath, updatedSvelteConfigString); + return fs.writeFileSync(path, content); } const prettierConfig = prettier.resolveConfig.sync(prettierConfigFile); - if (!prettierConfig) { - return fs.writeFileSync(svelteConfigPath, updatedSvelteConfigString); + return fs.writeFileSync(path, content); } - const prettySvelteConfig = prettier.format( - updatedSvelteConfigString, - prettierConfig - ); - - return fs.writeFileSync(svelteConfigPath, prettySvelteConfig); + const prettySvelteConfig = prettier.format(content, prettierConfig); + return fs.writeFileSync(path, prettySvelteConfig); } function createConfigNode(dir: string): Property { @@ -164,3 +189,59 @@ function createConfigNode(dir: string): Property { kind: "init" }; } + +function createAliasNode(): Property { + return { + type: "Property", + method: false, + shorthand: false, + computed: false, + key: { + type: "Identifier", + name: "alias" + }, + value: { + type: "ObjectExpression", + properties: createAliasProperties() + }, + kind: "init" + }; +} + +function createAliasProperties(): Property[] { + return [ + { + type: "Property", + method: false, + shorthand: false, + computed: false, + key: { + type: "Identifier", + name: "$components" + }, + value: { + type: "Literal", + value: "src/lib/components", + raw: '"src/lib/components"' + }, + kind: "init" + }, + { + type: "Property", + method: false, + shorthand: false, + computed: false, + key: { + type: "Literal", + value: "$components/*", + raw: '"$components/*"' + }, + value: { + type: "Literal", + value: "src/lib/components/*", + raw: '"src/lib/components/*"' + }, + kind: "init" + } + ]; +} diff --git a/packages/cli/src/utils/templates.ts b/packages/cli/src/utils/templates.ts index dd32811af..082a8337f 100644 --- a/packages/cli/src/utils/templates.ts +++ b/packages/cli/src/utils/templates.ts @@ -1,5 +1,3 @@ -import type { Node } from "estree-walker"; - export const STYLES = `@tailwind base; @tailwind components; @tailwind utilities; @@ -151,76 +149,3 @@ module.exports = { }, plugins: [require("tailwindcss-animate")], }`; - -export const SVELTE_CONFIG = `import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/kit/vite'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), - - kit: { - // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. - // If your environment is not supported or you settled on a specific environment, switch out the adapter. - // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter(), - alias: { - $components: "src/lib/components", - "$components/*": "src/lib/components/*" - } - } -}; - -export default config;`; - -export const FULL_ALIAS_NODE: Node = { - type: "Property", - method: false, - shorthand: false, - computed: false, - key: { - type: "Identifier", - name: "alias" - }, - value: { - type: "ObjectExpression", - properties: [ - { - type: "Property", - method: false, - shorthand: false, - computed: false, - key: { - type: "Identifier", - name: "$components" - }, - value: { - type: "Literal", - value: "src/lib/components", - raw: '"src/lib/components"' - }, - kind: "init" - }, - { - type: "Property", - method: false, - shorthand: false, - computed: false, - key: { - type: "Literal", - value: "$components/*", - raw: '"$components/*"' - }, - value: { - type: "Literal", - value: "src/lib/components/*", - raw: '"src/lib/components/*"' - }, - kind: "init" - } - ] - }, - kind: "init" -};