diff --git a/community-adder-template/src/config/adder.js b/community-adder-template/src/config/adder.js index 0836054c..74c611b3 100644 --- a/community-adder-template/src/config/adder.js +++ b/community-adder-template/src/config/adder.js @@ -10,7 +10,6 @@ export const adder = defineAdderConfig({ environments: { kit: true, svelte: true } }, options, - integrationType: 'inline', packages: [], files: [ { diff --git a/packages/adders/common.ts b/packages/adders/common.ts index 4255e8fe..a210e7d5 100644 --- a/packages/adders/common.ts +++ b/packages/adders/common.ts @@ -1,7 +1,14 @@ -import { imports, exports, common } from '@svelte-cli/core/js'; -import type { ScriptFileEditor } from '@svelte-cli/core'; +import { imports, exports, common, variables, functions } from '@svelte-cli/core/js'; +import { Walker, type AstKinds, type AstTypes, type ScriptFileEditor } from '@svelte-cli/core'; import type { Question } from '@svelte-cli/core/internal'; +export function createPrinter(...conditions: boolean[]) { + const printers = conditions.map((condition) => { + return (content: string, alt = '') => (condition ? content : alt); + }); + return printers; +} + export function addEslintConfigPrettier({ ast }: ScriptFileEditor>) { // if a default import for `eslint-plugin-svelte` already exists, then we'll use their specifier's name instead const importNodes = ast.body.filter((n) => n.type === 'ImportDeclaration'); @@ -57,3 +64,294 @@ export function addEslintConfigPrettier({ ast }: ScriptFileEditor n.type === 'TSModuleDeclaration') + .find((m) => m.global && m.declare); + + if (!globalDecl) { + globalDecl = common.statementFromString('declare global {}') as AstTypes.TSModuleDeclaration; + ast.body.push(globalDecl); + } + + if (globalDecl.body?.type !== 'TSModuleBlock') { + throw new Error('Unexpected body type of `declare global` in `src/app.d.ts`'); + } + + let app: AstTypes.TSModuleDeclaration | undefined; + let interfaceNode: AstTypes.TSInterfaceDeclaration | undefined; + + // prettier-ignore + Walker.walk(globalDecl as AstTypes.ASTNode, {}, { + TSModuleDeclaration(node, { next }) { + if (node.id.type === 'Identifier' && node.id.name === 'App') { + app = node; + } + next(); + }, + TSInterfaceDeclaration(node) { + if (node.id.type === 'Identifier' && node.id.name === name) { + interfaceNode = node; + } + }, + }); + + if (!app) { + app = common.statementFromString('namespace App {}') as AstTypes.TSModuleDeclaration; + globalDecl.body.body.push(app); + } + + if (app.body?.type !== 'TSModuleBlock') { + throw new Error('Unexpected body type of `namespace App` in `src/app.d.ts`'); + } + + if (!interfaceNode) { + // add the interface if it's missing + interfaceNode = common.statementFromString( + `interface ${name} {}` + ) as AstTypes.TSInterfaceDeclaration; + app.body.body.push(interfaceNode); + } + + return interfaceNode; +} + +export function hasTypeProp( + name: string, + node: AstTypes.TSInterfaceDeclaration['body']['body'][number] +) { + return ( + node.type === 'TSPropertySignature' && node.key.type === 'Identifier' && node.key.name === name + ); +} + +export function addHooksHandle( + ast: AstTypes.Program, + typescript: boolean, + newHandleName: string, + handleContent: string, + forceSeparateHandle: boolean = false +) { + if (typescript) { + imports.addNamed(ast, '@sveltejs/kit', { Handle: 'Handle' }, true); + } + + let isSpecifier: boolean = false; + let handleName = 'handle'; + let exportDecl: AstTypes.ExportNamedDeclaration | undefined; + let originalHandleDecl: AstKinds.DeclarationKind | undefined; + + // We'll first visit all of the named exports and grab their references if they export `handle`. + // This will grab export references for: + // `export { handle }` & `export { foo as handle }` + // `export const handle = ...`, & `export function handle() {...}` + // prettier-ignore + Walker.walk(ast as AstTypes.ASTNode, {}, { + ExportNamedDeclaration(node) { + let maybeHandleDecl: AstKinds.DeclarationKind | undefined; + + // `export { handle }` & `export { foo as handle }` + const handleSpecifier = node.specifiers?.find((s) => s.exported.name === 'handle'); + if (handleSpecifier) { + isSpecifier = true; + // we'll search for the local name in case it's aliased (e.g. `export { foo as handle }`) + handleName = handleSpecifier.local?.name ?? handleSpecifier.exported.name; + + // find the definition + const handleFunc = ast.body.find((n) => isFunctionDeclaration(n, handleName)); + const handleVar = ast.body.find((n) => isVariableDeclaration(n, handleName)); + + maybeHandleDecl = handleFunc ?? handleVar; + } + + maybeHandleDecl ??= node.declaration ?? undefined; + + // `export const handle` + if (maybeHandleDecl && isVariableDeclaration(maybeHandleDecl, handleName)) { + exportDecl = node; + originalHandleDecl = maybeHandleDecl; + } + + // `export function handle` + if (maybeHandleDecl && isFunctionDeclaration(maybeHandleDecl, handleName)) { + exportDecl = node; + originalHandleDecl = maybeHandleDecl; + } + }, + }); + + const newHandle = common.expressionFromString(handleContent); + if (common.hasNode(ast, newHandle)) return; + + // This is the straightforward case. If there's no existing `handle`, we'll just add one + // with the new handle's definition and exit + if (!originalHandleDecl || !exportDecl) { + // handle declaration doesn't exist, so we'll just create it with the hook + const newDecl = variables.declaration(ast, 'const', handleName, newHandle); + if (typescript) { + const declarator = newDecl.declarations[0] as AstTypes.VariableDeclarator; + variables.typeAnnotateDeclarator(declarator, 'Handle'); + } + + if (!forceSeparateHandle) exports.namedExport(ast, handleName, newDecl); + else { + const newDecl = variables.declaration(ast, 'const', newHandleName, newHandle); + if (typescript) { + const declarator = newDecl.declarations[0] as AstTypes.VariableDeclarator; + variables.typeAnnotateDeclarator(declarator, 'Handle'); + } + ast.body.push(newDecl); + + const handleDecl = variables.declaration( + ast, + 'const', + handleName, + common.expressionFromString(newHandleName) + ); + if (typescript) { + const declarator = handleDecl.declarations[0] as AstTypes.VariableDeclarator; + variables.typeAnnotateDeclarator(declarator, 'Handle'); + } + exports.namedExport(ast, handleName, handleDecl); + } + + return; + } + + // create the new handle + const newDecl = variables.declaration(ast, 'const', newHandleName, newHandle); + if (typescript) { + const declarator = newDecl.declarations[0] as AstTypes.VariableDeclarator; + variables.typeAnnotateDeclarator(declarator, 'Handle'); + } + + // check if `handle` is using a sequence + let sequence: AstTypes.CallExpression | undefined; + if (originalHandleDecl.type === 'VariableDeclaration') { + const handle = originalHandleDecl.declarations.find( + (d) => d.type === 'VariableDeclarator' && usingSequence(d, handleName) + ) as AstTypes.VariableDeclarator | undefined; + + sequence = handle?.init as AstTypes.CallExpression; + } + + // If `handle` is already using a `sequence`, then we'll just create the new handle and + // append the new handle name to the args of `sequence` + // e.g. `export const handle = sequence(some, other, handles, newHandle);` + if (sequence) { + const hasNewArg = sequence.arguments.some( + (arg) => arg.type === 'Identifier' && arg.name === newHandleName + ); + if (!hasNewArg) { + sequence.arguments.push(variables.identifier(newHandleName)); + } + + // removes the declarations so we can append them in the correct order + ast.body = ast.body.filter( + (n) => n !== originalHandleDecl && n !== exportDecl && n !== newDecl + ); + if (isSpecifier) { + // if export specifiers are being used (e.g. `export { handle }`), then we'll want + // need to also append original handle declaration as it's not part of the export declaration + ast.body.push(newDecl, originalHandleDecl, exportDecl); + } else { + ast.body.push(newDecl, exportDecl); + } + + return; + } + + // At this point, the existing `handle` doesn't call `sequence`, so we'll need to rename the original + // `handle` and create a new `handle` that uses `sequence` + // e.g. `const handle = sequence(originalHandle, newHandle);` + const NEW_HANDLE_NAME = 'originalHandle'; + const sequenceCall = functions.callByIdentifier('sequence', [NEW_HANDLE_NAME, newHandleName]); + const newHandleDecl = variables.declaration(ast, 'const', handleName, sequenceCall); + + imports.addNamed(ast, '@sveltejs/kit/hooks', { sequence: 'sequence' }); + + let renameRequired = false; + // rename `export const handle` + if (originalHandleDecl && isVariableDeclaration(originalHandleDecl, handleName)) { + const handle = getVariableDeclarator(originalHandleDecl, handleName); + if (handle && handle.id.type === 'Identifier' && handle.init?.type !== 'Identifier') { + renameRequired = true; + handle.id.name = NEW_HANDLE_NAME; + } + } + // rename `export function handle` + if (originalHandleDecl && isFunctionDeclaration(originalHandleDecl, handleName)) { + renameRequired = true; + originalHandleDecl.id!.name = NEW_HANDLE_NAME; + } + + // removes all declarations so that we can re-append them in the correct order + ast.body = ast.body.filter((n) => n !== originalHandleDecl && n !== exportDecl && n !== newDecl); + + if (isSpecifier) { + ast.body.push(originalHandleDecl, newDecl, newHandleDecl, exportDecl); + } + + if (exportDecl.declaration && renameRequired) { + // when we re-append the declarations, we only want to add the declaration + // of the (now renamed) original `handle` _without_ the `export` keyword: + // e.g. `const originalHandle = ...;` + ast.body.push(exportDecl.declaration, newDecl); + // `export const handle = sequence(originalHandle, newHandle);` + exports.namedExport(ast, handleName, newHandleDecl); + } else if (exportDecl.declaration && isVariableDeclaration(originalHandleDecl, handleName)) { + // if the previous value of `export const handle = ...` was an identifier + // there is no need to rename the handle, we just need to add it to the sequence + const variableDeclarator = getVariableDeclarator(originalHandleDecl, handleName); + const sequenceCall = functions.callByIdentifier('sequence', [ + (variableDeclarator?.init as AstTypes.Identifier).name, + newHandleName + ]); + const newHandleDecl = variables.declaration(ast, 'const', handleName, sequenceCall); + if (typescript) { + const declarator = newHandleDecl.declarations[0] as AstTypes.VariableDeclarator; + variables.typeAnnotateDeclarator(declarator, 'Handle'); + } + ast.body.push(newDecl); + exports.namedExport(ast, handleName, newHandleDecl); + } +} + +function usingSequence(node: AstTypes.VariableDeclarator, handleName: string) { + return ( + node.id.type === 'Identifier' && + node.id.name === handleName && + node.init?.type === 'CallExpression' && + node.init.callee.type === 'Identifier' && + node.init.callee.name === 'sequence' + ); +} + +function isVariableDeclaration( + node: AstTypes.ASTNode, + variableName: string +): node is AstTypes.VariableDeclaration { + return ( + node.type === 'VariableDeclaration' && getVariableDeclarator(node, variableName) !== undefined + ); +} + +function getVariableDeclarator( + node: AstTypes.VariableDeclaration, + handleName: string +): AstTypes.VariableDeclarator | undefined { + return node.declarations.find( + (d) => d.type === 'VariableDeclarator' && d.id.type === 'Identifier' && d.id.name === handleName + ) as AstTypes.VariableDeclarator | undefined; +} + +function isFunctionDeclaration( + node: AstTypes.ASTNode, + funcName: string +): node is AstTypes.FunctionDeclaration { + return node.type === 'FunctionDeclaration' && node.id?.name === funcName; +} diff --git a/packages/adders/drizzle/config/adder.ts b/packages/adders/drizzle/config/adder.ts index fcc46c28..ae4768f7 100644 --- a/packages/adders/drizzle/config/adder.ts +++ b/packages/adders/drizzle/config/adder.ts @@ -21,7 +21,6 @@ export const adder = defineAdderConfig({ } }, options: availableOptions, - integrationType: 'inline', packages: [ { name: 'drizzle-orm', version: '^0.33.0', dev: false }, { name: 'drizzle-kit', version: '^0.22.0', dev: true }, @@ -328,15 +327,14 @@ export const adder = defineAdderConfig({ } } ], - nextSteps: ({ options, colors }) => { - const highlight = (str: string) => colors.bold(colors.cyan(str)); + nextSteps: ({ options, highlighter }) => { const steps = [ - `You will need to set ${colors.yellow('DATABASE_URL')} in your production environment` + `You will need to set ${highlighter.env('DATABASE_URL')} in your production environment` ]; if (options.docker) { - steps.push(`Run ${highlight('npm run db:start')} to start the docker container`); + steps.push(`Run ${highlighter.command('npm run db:start')} to start the docker container`); } - steps.push(`To update your DB schema, run ${highlight('npm run db:push')}`); + steps.push(`To update your DB schema, run ${highlighter.command('npm run db:push')}`); return steps; } diff --git a/packages/adders/eslint/config/adder.ts b/packages/adders/eslint/config/adder.ts index 0ccf5bbb..9926ef69 100644 --- a/packages/adders/eslint/config/adder.ts +++ b/packages/adders/eslint/config/adder.ts @@ -18,7 +18,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [ { name: 'eslint', version: '^9.7.0', dev: true }, { name: '@types/eslint', version: '^9.6.0', dev: true }, diff --git a/packages/adders/lucia/config/adder.ts b/packages/adders/lucia/config/adder.ts index eac7b959..a24cc8ae 100644 --- a/packages/adders/lucia/config/adder.ts +++ b/packages/adders/lucia/config/adder.ts @@ -2,7 +2,8 @@ import { options } from './options.ts'; import { colors, dedent, defineAdderConfig, log, Walker } from '@svelte-cli/core'; import { common, exports, imports, variables, object, functions } from '@svelte-cli/core/js'; // eslint-disable-next-line no-duplicate-imports -import type { AstKinds, AstTypes } from '@svelte-cli/core/js'; +import type { AstTypes } from '@svelte-cli/core/js'; +import { addHooksHandle, addGlobalAppInterface, hasTypeProp } from '../../common.ts'; const LUCIA_ADAPTER = { mysql: 'DrizzleMySQLAdapter', @@ -34,7 +35,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [ { name: 'lucia', version: '^3.2.0', dev: false }, { name: '@lucia-auth/adapter-drizzle', version: '^1.1.0', dev: false }, @@ -239,68 +239,10 @@ export const adder = defineAdderConfig({ condition: ({ typescript }) => typescript, contentType: 'script', content: ({ ast }) => { - const globalDecl = ast.body - .filter((n) => n.type === 'TSModuleDeclaration') - .find((m) => m.global && m.declare); - - if (globalDecl?.body?.type !== 'TSModuleBlock') { - throw new Error('Unexpected body type of `declare global` in `src/app.d.ts`'); - } - - if (!globalDecl) { - const decl = common.statementFromString(` - declare global { - namespace App { - interface Locals { - user: import('lucia').User | null; - session: import('lucia').Session | null; - } - } - }`); - ast.body.push(decl); - return; - } - - let app: AstTypes.TSModuleDeclaration | undefined; - let locals: AstTypes.TSInterfaceDeclaration | undefined; - - // prettier-ignore - Walker.walk(globalDecl as AstTypes.ASTNode, {}, { - TSModuleDeclaration(node, { next }) { - if (node.id.type === 'Identifier' && node.id.name === 'App') { - app = node; - } - next(); - }, - TSInterfaceDeclaration(node) { - if (node.id.type === 'Identifier' && node.id.name === 'Locals') { - locals = node; - } - }, - }); - - if (!app) { - app ??= common.statementFromString(` - namespace App { - interface Locals { - user: import('lucia').User | null; - session: import('lucia').Session | null; - } - }`) as AstTypes.TSModuleDeclaration; - globalDecl.body.body.push(app); - return; - } - - if (app.body?.type !== 'TSModuleBlock') { - throw new Error('Unexpected body type of `namespace App` in `src/app.d.ts`'); - } + const locals = addGlobalAppInterface(ast, 'Locals'); if (!locals) { - // add Locals interface it if it's missing - locals = common.statementFromString( - 'interface Locals {}' - ) as AstTypes.TSInterfaceDeclaration; - app.body.body.push(locals); + throw new Error('Failed detecting `locals` interface in `src/app.d.ts`'); } const user = locals.body.body.find((prop) => hasTypeProp('user', prop)); @@ -320,154 +262,7 @@ export const adder = defineAdderConfig({ content: ({ ast, typescript }) => { imports.addNamed(ast, '$lib/server/auth.js', { lucia: 'lucia' }); - if (typescript) { - imports.addNamed(ast, '@sveltejs/kit', { Handle: 'Handle' }, true); - } - - let isSpecifier: boolean = false; - let handleName = 'handle'; - let exportDecl: AstTypes.ExportNamedDeclaration | undefined; - let originalHandleDecl: AstKinds.DeclarationKind | undefined; - - // We'll first visit all of the named exports and grab their references if they export `handle`. - // This will grab export references for: - // `export { handle }` & `export { foo as handle }` - // `export const handle = ...`, & `export function handle() {...}` - // prettier-ignore - Walker.walk(ast as AstTypes.ASTNode, {}, { - ExportNamedDeclaration(node) { - let maybeHandleDecl: AstKinds.DeclarationKind | undefined; - - // `export { handle }` & `export { foo as handle }` - const handleSpecifier = node.specifiers?.find((s) => s.exported.name === 'handle'); - if (handleSpecifier) { - isSpecifier = true; - // we'll search for the local name in case it's aliased (e.g. `export { foo as handle }`) - handleName = handleSpecifier.local?.name ?? handleSpecifier.exported.name; - - // find the definition - const handleFunc = ast.body.find((n) => isFunctionDeclaration(n, handleName)); - const handleVar = ast.body.find((n) => isVariableDeclaration(n, handleName)); - - maybeHandleDecl = handleFunc ?? handleVar; - } - - maybeHandleDecl ??= node.declaration ?? undefined; - - // `export const handle` - if (maybeHandleDecl && isVariableDeclaration(maybeHandleDecl, handleName)) { - exportDecl = node; - originalHandleDecl = maybeHandleDecl; - } - - // `export function handle` - if (maybeHandleDecl && isFunctionDeclaration(maybeHandleDecl, handleName)) { - exportDecl = node; - originalHandleDecl = maybeHandleDecl; - } - }, - }); - - const authHandle = common.expressionFromString(getAuthHandleContent()); - if (common.hasNode(ast, authHandle)) return; - - // This is the straightforward case. If there's no existing `handle`, we'll just add one - // with the `auth` handle's definition and exit - if (!originalHandleDecl || !exportDecl) { - // handle declaration doesn't exist, so we'll just create it with the hook - const authDecl = variables.declaration(ast, 'const', handleName, authHandle); - if (typescript) { - const declarator = authDecl.declarations[0] as AstTypes.VariableDeclarator; - variables.typeAnnotateDeclarator(declarator, 'Handle'); - } - - exports.namedExport(ast, handleName, authDecl); - - return; - } - - // create the `auth` handle - const authName = 'auth'; - const authDecl = variables.declaration(ast, 'const', authName, authHandle); - if (typescript) { - const declarator = authDecl.declarations[0] as AstTypes.VariableDeclarator; - variables.typeAnnotateDeclarator(declarator, 'Handle'); - } - - // check if `handle` is using a sequence - let sequence: AstTypes.CallExpression | undefined; - if (originalHandleDecl.type === 'VariableDeclaration') { - const handle = originalHandleDecl.declarations.find( - (d) => d.type === 'VariableDeclarator' && usingSequence(d, handleName) - ) as AstTypes.VariableDeclarator | undefined; - - sequence = handle?.init as AstTypes.CallExpression; - } - - // If `handle` is already using a `sequence`, then we'll just create the `auth` handle and - // append `auth` to the args of `sequence` - // e.g. `export const handle = sequence(some, other, handles, auth);` - if (sequence) { - const hasAuthArg = sequence.arguments.some( - (arg) => arg.type === 'Identifier' && arg.name === authName - ); - if (!hasAuthArg) { - sequence.arguments.push(variables.identifier(authName)); - } - - // removes the declarations so we can append them in the correct order - ast.body = ast.body.filter( - (n) => n !== originalHandleDecl && n !== exportDecl && n !== authDecl - ); - if (isSpecifier) { - // if export specifiers are being used (e.g. `export { handle }`), then we'll want - // need to also append original handle declaration as it's not part of the export declaration - ast.body.push(authDecl, originalHandleDecl, exportDecl); - } else { - ast.body.push(authDecl, exportDecl); - } - - return; - } - - // At this point, the existing `handle` doesn't call `sequence`, so we'll need to rename the original - // `handle` and create a new `handle` that uses `sequence` - // e.g. `const handle = sequence(originalHandle, auth);` - const NEW_HANDLE_NAME = 'originalHandle'; - const sequenceCall = functions.callByIdentifier('sequence', [NEW_HANDLE_NAME, authName]); - const newHandleDecl = variables.declaration(ast, 'const', handleName, sequenceCall); - - imports.addNamed(ast, '@sveltejs/kit/hooks', { sequence: 'sequence' }); - - // rename `export const handle` - if (originalHandleDecl && isVariableDeclaration(originalHandleDecl, handleName)) { - const handle = getVariableDeclarator(originalHandleDecl, handleName); - if (handle && handle.id.type === 'Identifier') { - handle.id.name = NEW_HANDLE_NAME; - } - } - // rename `export function handle` - if (originalHandleDecl && isFunctionDeclaration(originalHandleDecl, handleName)) { - originalHandleDecl.id!.name = NEW_HANDLE_NAME; - } - - // removes all declarations so that we can re-append them in the correct order - ast.body = ast.body.filter( - (n) => n !== originalHandleDecl && n !== exportDecl && n !== authDecl - ); - - if (isSpecifier) { - ast.body.push(originalHandleDecl, authDecl, newHandleDecl, exportDecl); - } - - if (exportDecl.declaration) { - // when we re-append the declarations, we only want to add the declaration - // of the (now renamed) original `handle` _without_ the `export` keyword: - // e.g. `const originalHandle = ...;` - ast.body.push(exportDecl.declaration, authDecl); - // `export const handle = sequence(originalHandle, auth);` - exports.namedExport(ast, handleName, newHandleDecl); - } + addHooksHandle(ast, typescript, 'auth', getAuthHandleContent()); } }, // DEMO @@ -729,10 +524,10 @@ export const adder = defineAdderConfig({ } } ], - nextSteps: ({ colors, options }) => { - const steps = [`Run ${colors.bold(colors.cyan('npm run db:push'))} to update your database`]; + nextSteps: ({ highlighter, options }) => { + const steps = [`Run ${highlighter.command('npm run db:push')} to update your database`]; if (options.demo) { - steps.push(`Visit ${colors.bold('/demo')} route to view the demo`); + steps.push(`Visit ${highlighter.route('/demo')} route to view the demo`); } return steps; @@ -769,47 +564,6 @@ function createLuciaType(name: string): AstTypes.TSInterfaceBody['body'][number] }; } -function hasTypeProp(name: string, node: AstTypes.TSInterfaceDeclaration['body']['body'][number]) { - return ( - node.type === 'TSPropertySignature' && node.key.type === 'Identifier' && node.key.name === name - ); -} - -function usingSequence(node: AstTypes.VariableDeclarator, handleName: string) { - return ( - node.id.type === 'Identifier' && - node.id.name === handleName && - node.init?.type === 'CallExpression' && - node.init.callee.type === 'Identifier' && - node.init.callee.name === 'sequence' - ); -} - -function isVariableDeclaration( - node: AstTypes.ASTNode, - variableName: string -): node is AstTypes.VariableDeclaration { - return ( - node.type === 'VariableDeclaration' && getVariableDeclarator(node, variableName) !== undefined - ); -} - -function getVariableDeclarator( - node: AstTypes.VariableDeclaration, - handleName: string -): AstTypes.VariableDeclarator | undefined { - return node.declarations.find( - (d) => d.type === 'VariableDeclarator' && d.id.type === 'Identifier' && d.id.name === handleName - ) as AstTypes.VariableDeclarator | undefined; -} - -function isFunctionDeclaration( - node: AstTypes.ASTNode, - funcName: string -): node is AstTypes.FunctionDeclaration { - return node.type === 'FunctionDeclaration' && node.id?.name === funcName; -} - function getAuthHandleContent() { return ` async ({ event, resolve }) => { diff --git a/packages/adders/mdsvex/config/adder.ts b/packages/adders/mdsvex/config/adder.ts index c71e8ba8..b3608cc8 100644 --- a/packages/adders/mdsvex/config/adder.ts +++ b/packages/adders/mdsvex/config/adder.ts @@ -15,7 +15,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [{ name: 'mdsvex', version: '^0.11.2', dev: true }], files: [ { diff --git a/packages/adders/playwright/config/adder.ts b/packages/adders/playwright/config/adder.ts index 488ac7c4..a2f2f478 100644 --- a/packages/adders/playwright/config/adder.ts +++ b/packages/adders/playwright/config/adder.ts @@ -17,7 +17,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [{ name: '@playwright/test', version: '^1.45.3', dev: true }], files: [ { diff --git a/packages/adders/prettier/config/adder.ts b/packages/adders/prettier/config/adder.ts index 355ea888..4db38180 100644 --- a/packages/adders/prettier/config/adder.ts +++ b/packages/adders/prettier/config/adder.ts @@ -15,7 +15,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [ { name: 'prettier', version: '^3.3.2', dev: true }, { name: 'prettier-plugin-svelte', version: '^3.2.6', dev: true }, diff --git a/packages/adders/routify/config/adder.ts b/packages/adders/routify/config/adder.ts index 6565038f..820475b5 100644 --- a/packages/adders/routify/config/adder.ts +++ b/packages/adders/routify/config/adder.ts @@ -16,7 +16,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [{ name: '@roxi/routify', version: 'next', dev: true }], files: [ { diff --git a/packages/adders/storybook/config/adder.ts b/packages/adders/storybook/config/adder.ts index 36503e13..342f564d 100644 --- a/packages/adders/storybook/config/adder.ts +++ b/packages/adders/storybook/config/adder.ts @@ -20,8 +20,14 @@ export const adder = defineAdderConfig({ documentation: 'https://storybook.js.org/docs/get-started' } }, - + packages: [], + scripts: [ + { + description: 'applies storybook', + args: ['storybook@latest', 'init', '--skip-install', '--no-dev'], + stdio: 'inherit' + } + ], options, - integrationType: 'external', - command: 'storybook@latest init --skip-install --no-dev' + files: [] }); diff --git a/packages/adders/tailwindcss/config/adder.ts b/packages/adders/tailwindcss/config/adder.ts index 7d4c050f..7b63d5ae 100644 --- a/packages/adders/tailwindcss/config/adder.ts +++ b/packages/adders/tailwindcss/config/adder.ts @@ -18,7 +18,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [ { name: 'tailwindcss', version: '^3.4.9', dev: true }, { name: 'autoprefixer', version: '^10.4.20', dev: true }, diff --git a/packages/adders/vitest/config/adder.ts b/packages/adders/vitest/config/adder.ts index fd0378ba..7a0473a6 100644 --- a/packages/adders/vitest/config/adder.ts +++ b/packages/adders/vitest/config/adder.ts @@ -15,7 +15,6 @@ export const adder = defineAdderConfig({ } }, options, - integrationType: 'inline', packages: [{ name: 'vitest', version: '^2.0.4', dev: true }], files: [ { diff --git a/packages/cli/commands/add.ts b/packages/cli/commands/add.ts index eab089ad..245867ad 100644 --- a/packages/cli/commands/add.ts +++ b/packages/cli/commands/add.ts @@ -19,22 +19,17 @@ import { createOrUpdateFiles, createWorkspace, installPackages, - TESTING + getHighlighter } from '@svelte-cli/core/internal'; -import type { - AdderWithoutExplicitArgs, - ExternalAdderConfig, - InlineAdderConfig, - OptionDefinition, - OptionValues -} from '@svelte-cli/core'; +import type { AdderWithoutExplicitArgs, OptionValues } from '@svelte-cli/core'; import * as common from '../common.js'; import { Directive, downloadPackage, getPackageJSON } from '../utils/fetch-packages.js'; const AddersSchema = v.array(v.string()); const AdderOptionFlagsSchema = v.object({ tailwindcss: v.optional(v.array(v.string())), - drizzle: v.optional(v.array(v.string())) + drizzle: v.optional(v.array(v.string())), + supabase: v.optional(v.array(v.string())) }); const OptionsSchema = v.strictObject({ cwd: v.string(), @@ -342,12 +337,11 @@ export async function runAddCommand(options: Options, adders: string[]): Promise // check if the dependent adder has already been installed let installed = false; - if (dependent.config.integrationType === 'inline') { - installed = dependent.config.packages.every( - // we'll skip the conditions since we don't have any options to supply it - (p) => p.condition !== undefined || !!workspace.dependencies[p.name] - ); - } + installed = dependent.config.packages.every( + // we'll skip the conditions since we don't have any options to supply it + (p) => p.condition !== undefined || !!workspace.dependencies[p.name] + ); + if (installed) continue; // prompt to install the dependent @@ -480,11 +474,13 @@ export async function runAddCommand(options: Options, adders: string[]): Promise } } + const highlighter = getHighlighter(); + // print next steps const nextStepsMsg = selectedAdders - .filter(({ adder }) => adder.config.integrationType === 'inline' && adder.config.nextSteps) - .map(({ adder }) => adder.config as InlineAdderConfig) - .map((config) => { + .filter(({ adder }) => adder.config.nextSteps) + .map(({ adder }) => { + const config = adder.config; const metadata = config.metadata; let adderMessage = ''; if (selectedAdders.length > 1) { @@ -492,10 +488,9 @@ export async function runAddCommand(options: Options, adders: string[]): Promise } const adderNextSteps = config.nextSteps!({ + ...workspace, options: official[metadata.id], - cwd: options.cwd, - colors: pc, - docs: metadata.website?.documentation + highlighter }); adderMessage += `- ${adderNextSteps.join('\n- ')}`; return adderMessage; @@ -549,45 +544,45 @@ export async function installAdders({ const filesToFormat = new Set(); for (const { config } of details) { const adderId = config.metadata.id; + // TODO: make this sync const workspace = createWorkspace(cwd); workspace.options = official[adderId] ?? community[adderId]; // execute adders - if (config.integrationType === 'inline') { - const pkgPath = installPackages(config, workspace); - filesToFormat.add(pkgPath); - const changedFiles = createOrUpdateFiles(config.files, workspace); - changedFiles.forEach((file) => filesToFormat.add(file)); - } else if (config.integrationType === 'external') { - await processExternalAdder(config, cwd); - } else { - throw new Error('Unknown integration type'); + const pkgPath = installPackages(config, workspace); + filesToFormat.add(pkgPath); + const changedFiles = createOrUpdateFiles(config.files, workspace); + changedFiles.forEach((file) => filesToFormat.add(file)); + + if (config.scripts && config.scripts.length > 0) { + const name = config.metadata.name; + p.log.step(`Running external command ${pc.gray(`(${name})`)}`); + + for (const script of config.scripts) { + if (script.condition?.(workspace) === false) continue; + + const { command, args } = resolveCommand(workspace.packageManager, 'execute', script.args)!; + // adding --yes as the first parameter helps avoiding the "Need to install the following packages:" message + if (workspace.packageManager === 'npm') args.unshift('--yes'); + + try { + await exec(command, args, { nodeOptions: { cwd: workspace.cwd, stdio: script.stdio } }); + } catch (error) { + const typedError = error as Error; + throw new Error( + `Failed to execute scripts '${script.description}': ` + typedError.message + ); + } + } + + p.log.success(`Finished running ${name}`); } } return Array.from(filesToFormat); } -async function processExternalAdder( - config: ExternalAdderConfig, - cwd: string -) { - if (!TESTING) p.log.message(`Executing external command ${pc.gray(`(${config.metadata.id})`)}`); - - try { - const pm = await common.guessPackageManager(cwd); - const cmd = resolveCommand(pm, 'execute', config.command.split(' '))!; - const env = { ...process.env, ...config.environment }; - await exec(cmd.command, cmd.args, { - nodeOptions: { cwd, env, stdio: TESTING ? 'pipe' : 'inherit' } - }); - } catch (error) { - const typedError = error as Error; - throw new Error('Failed executing external command: ' + typedError.message); - } -} - /** * Dedupes and transforms aliases into their respective adder id */ diff --git a/packages/cli/commands/check.ts b/packages/cli/commands/check.ts index 2a0a4a2c..205bc7ec 100644 --- a/packages/cli/commands/check.ts +++ b/packages/cli/commands/check.ts @@ -3,7 +3,7 @@ import pc from 'picocolors'; import * as resolve from 'empathic/resolve'; import { Command } from 'commander'; import { resolveCommand } from 'package-manager-detector/commands'; -import { getUserAgent } from '../common.ts'; +import { getUserAgent } from '@svelte-cli/core/internal'; export const check = new Command('check') .description('a CLI for checking your Svelte code') diff --git a/packages/cli/commands/create.ts b/packages/cli/commands/create.ts index 89ee1f26..aa89d9c6 100644 --- a/packages/cli/commands/create.ts +++ b/packages/cli/commands/create.ts @@ -12,6 +12,7 @@ import { } from '@svelte-cli/create'; import * as common from '../common.js'; import { runAddCommand } from './add.js'; +import { detectPackageManager } from '@svelte-cli/core/internal'; const langs = ['typescript', 'checkjs', 'none'] as const; const templateChoices = templates.map((t) => t.name); @@ -47,7 +48,7 @@ export const create = new Command('create') let i = 1; const initialSteps: string[] = []; const relative = path.relative(process.cwd(), directory); - const pm = await common.guessPackageManager(cwd); + const pm = await detectPackageManager(cwd); if (relative !== '') { initialSteps.push(`${i++}: ${highlight(`cd ${relative}`)}`); } diff --git a/packages/cli/commands/migrate.ts b/packages/cli/commands/migrate.ts index 73232190..5699628d 100644 --- a/packages/cli/commands/migrate.ts +++ b/packages/cli/commands/migrate.ts @@ -1,7 +1,7 @@ import { execSync } from 'node:child_process'; import { Command } from 'commander'; import { resolveCommand } from 'package-manager-detector'; -import { getUserAgent } from '../common.ts'; +import { getUserAgent } from '@svelte-cli/core/internal'; export const migrate = new Command('migrate') .description('a CLI for migrating Svelte(Kit) codebases') diff --git a/packages/cli/common.ts b/packages/cli/common.ts index 17b107fb..e9ec7c65 100644 --- a/packages/cli/common.ts +++ b/packages/cli/common.ts @@ -4,8 +4,9 @@ import { exec } from 'tinyexec'; import * as p from '@svelte-cli/clack-prompts'; import { detect, AGENTS, type AgentName } from 'package-manager-detector'; import { COMMANDS, constructCommand, resolveCommand } from 'package-manager-detector/commands'; -import type { AdderWithoutExplicitArgs, Precondition } from '@svelte-cli/core'; import type { Argument, HelpConfiguration, Option } from 'commander'; +import type { AdderWithoutExplicitArgs, Precondition } from '@svelte-cli/core'; +import { detectPackageManager, getUserAgent } from '@svelte-cli/core/internal'; export const helpConfig: HelpConfiguration = { argumentDescription: formatDescription, @@ -41,7 +42,7 @@ export async function runCommand(action: MaybePromise) { } export async function formatFiles(cwd: string, paths: string[]): Promise { - const pm = await guessPackageManager(cwd); + const pm = await detectPackageManager(cwd); const args = ['prettier', '--write', '--ignore-unknown', ...paths]; const cmd = resolveCommand(pm, 'execute-local', args)!; await exec(cmd.command, cmd.args, { @@ -89,25 +90,6 @@ export async function suggestInstallingDependencies(cwd: string): Promise<'insta return 'installed'; } -/** - * Guesses the package manager based on the detected lockfile or user-agent. - * If neither of those return valid package managers, it falls back to `npm`. - */ -export async function guessPackageManager(cwd: string): Promise { - if (packageManager) return packageManager; - const pm = await detect({ cwd }); - return pm?.name ?? getUserAgent() ?? 'npm'; -} - -export function getUserAgent() { - const userAgent = process.env.npm_config_user_agent; - if (!userAgent) return undefined; - const pmSpec = userAgent.split(' ')[0]; - const separatorPos = pmSpec.lastIndexOf('/'); - const name = pmSpec.substring(0, separatorPos) as AgentName; - return AGENTS.includes(name) ? name : undefined; -} - async function installDependencies(command: string, args: string[], cwd: string) { try { await exec(command, args, { nodeOptions: { cwd } }); diff --git a/packages/cli/package.json b/packages/cli/package.json index 3b9326c7..98c8202e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,7 +36,7 @@ "@types/tar-fs": "^2.0.4", "commander": "^12.1.0", "empathic": "^1.0.0", - "package-manager-detector": "^0.2.1", + "package-manager-detector": "^0.2.2", "picocolors": "^1.1.0", "tar-fs": "^3.0.6", "tinyexec": "^0.3.0", diff --git a/packages/core/adder/config.ts b/packages/core/adder/config.ts index 918058d4..9c5d3eaa 100644 --- a/packages/core/adder/config.ts +++ b/packages/core/adder/config.ts @@ -1,12 +1,10 @@ import type { OptionDefinition, OptionValues, Question } from './options.ts'; import type { FileType } from '../files/processors.ts'; import type { Workspace } from '../files/workspace.ts'; -import type { Colors } from 'picocolors/types.ts'; export type ConditionDefinition = ( Workspace: Workspace ) => boolean; -export type ConditionDefinitionWithoutExplicitArgs = ConditionDefinition>; export type WebsiteMetadata = { logo: string; @@ -35,36 +33,36 @@ export type PackageDefinition = { condition?: ConditionDefinition; }; -export type BaseAdderConfig = { +export type Scripts = { + description: string; + args: string[]; + stdio: 'inherit' | 'pipe'; + condition?: ConditionDefinition; +}; + +export type AdderConfig = { metadata: AdderConfigMetadata; options: Args; runsAfter?: string[]; dependsOn?: string[]; - integrationType: string; -}; - -export type InlineAdderConfig = BaseAdderConfig & { - integrationType: 'inline'; packages: Array>; + scripts?: Array>; files: Array>; - nextSteps?: (data: { - options: OptionValues; - cwd: string; - colors: Colors; - docs: string | undefined; - }) => string[]; + nextSteps?: ( + data: { + highlighter: Highlighter; + } & Workspace + ) => string[]; }; -export type ExternalAdderConfig = BaseAdderConfig & { - integrationType: 'external'; - command: string; - environment?: Record; +export type Highlighter = { + path: (str: string) => string; + command: (str: string) => string; + website: (str: string) => string; + route: (str: string) => string; + env: (str: string) => string; }; -export type AdderConfig = - | InlineAdderConfig - | ExternalAdderConfig; - export function defineAdderConfig( config: AdderConfig ): AdderConfig { diff --git a/packages/core/adder/options.ts b/packages/core/adder/options.ts index 73a469c7..c5805ae3 100644 --- a/packages/core/adder/options.ts +++ b/packages/core/adder/options.ts @@ -32,7 +32,7 @@ export type BaseQuestion = { * When this condition explicitly returns `false`, the question's value will * always be `undefined` and will not fallback to the specified `default` value. */ - condition?: (options: OptionValues) => boolean; + condition?: (options: any) => boolean; // TODO: we want to type `options` similar to OptionValues so that its option values can be inferred }; diff --git a/packages/core/files/utils.ts b/packages/core/files/utils.ts index ec157e43..b207db40 100644 --- a/packages/core/files/utils.ts +++ b/packages/core/files/utils.ts @@ -1,9 +1,9 @@ import fs from 'node:fs'; import path from 'node:path'; +import pc from 'picocolors'; import { parseJson, serializeJson } from '@svelte-cli/ast-tooling'; -import type { InlineAdderConfig } from '../adder/config.ts'; -import type { OptionDefinition } from '../adder/options.ts'; -import type { Workspace, WorkspaceWithoutExplicitArgs } from './workspace.ts'; +import type { AdderConfig, Highlighter } from '../adder/config.ts'; +import type { Workspace } from './workspace.ts'; export type Package = { name: string; @@ -15,7 +15,7 @@ export type Package = { keywords?: string[]; }; -export function getPackageJson(workspace: WorkspaceWithoutExplicitArgs): { +export function getPackageJson(workspace: Workspace): { text: string; data: Package; } { @@ -39,7 +39,7 @@ export function getPackageJson(workspace: WorkspaceWithoutExplicitArgs): { }; } -export function readFile(workspace: WorkspaceWithoutExplicitArgs, filePath: string): string { +export function readFile(workspace: Workspace, filePath: string): string { const fullFilePath = getFilePath(workspace.cwd, filePath); if (!fileExistsWorkspace(workspace, filePath)) { @@ -51,10 +51,7 @@ export function readFile(workspace: WorkspaceWithoutExplicitArgs, filePath: stri return text; } -export function installPackages( - config: InlineAdderConfig, - workspace: Workspace -): string { +export function installPackages(config: AdderConfig, workspace: Workspace): string { const { text: originalText, data } = getPackageJson(workspace); for (const dependency of config.packages) { @@ -93,11 +90,7 @@ function alphabetizeProperties(obj: Record) { return orderedObj; } -export function writeFile( - workspace: WorkspaceWithoutExplicitArgs, - filePath: string, - content: string -): void { +export function writeFile(workspace: Workspace, filePath: string, content: string): void { const fullFilePath = getFilePath(workspace.cwd, filePath); const fullDirectoryPath = path.dirname(fullFilePath); @@ -110,10 +103,7 @@ export function writeFile( fs.writeFileSync(fullFilePath, content, 'utf8'); } -export function fileExistsWorkspace( - workspace: WorkspaceWithoutExplicitArgs, - filePath: string -): boolean { +export function fileExistsWorkspace(workspace: Workspace, filePath: string): boolean { const fullFilePath = getFilePath(workspace.cwd, filePath); return fs.existsSync(fullFilePath); } @@ -128,3 +118,13 @@ export const commonFilePaths = { tsconfig: 'tsconfig.json', viteConfigTS: 'vite.config.ts' } as const; + +export function getHighlighter(): Highlighter { + return { + command: (str) => pc.bold(pc.cyanBright(str)), + env: (str) => pc.yellow(str), + path: (str) => pc.green(str), + route: (str) => pc.bold(str), + website: (str) => pc.whiteBright(str) + }; +} diff --git a/packages/core/files/workspace.ts b/packages/core/files/workspace.ts index 128a584a..cef874d0 100644 --- a/packages/core/files/workspace.ts +++ b/packages/core/files/workspace.ts @@ -2,11 +2,13 @@ import fs from 'node:fs'; import path from 'node:path'; import * as find from 'empathic/find'; import * as resolve from 'empathic/resolve'; +import { AGENTS, detectSync, type AgentName } from 'package-manager-detector'; import { type AstTypes, parseScript } from '@svelte-cli/ast-tooling'; import { TESTING } from '../env.ts'; import { common, object } from '../tooling/js/index.ts'; import { commonFilePaths, getPackageJson, readFile } from './utils.ts'; -import type { OptionDefinition, OptionValues, Question } from '../adder/options.ts'; +import type { OptionDefinition, OptionValues } from '../adder/options.ts'; +import process from 'node:process'; export type Workspace = { options: OptionValues; @@ -15,10 +17,9 @@ export type Workspace = { prettier: boolean; typescript: boolean; kit: { libDirectory: string; routesDirectory: string } | undefined; + packageManager: AgentName; }; -export type WorkspaceWithoutExplicitArgs = Workspace>; - export function createEmptyWorkspace() { return { options: {}, @@ -48,6 +49,7 @@ export function createWorkspace(cwd: string): Wor workspace.dependencies = { ...packageJson.devDependencies, ...packageJson.dependencies }; workspace.typescript = usesTypescript; workspace.prettier = Boolean(resolve.from(cwd, 'prettier', true)); + workspace.packageManager = detectPackageManager(cwd); if ('@sveltejs/kit' in workspace.dependencies) workspace.kit = parseKitOptions(workspace); for (const [key, value] of Object.entries(workspace.dependencies)) { // removes the version ranges (e.g. `^` is removed from: `^9.0.0`) @@ -57,7 +59,7 @@ export function createWorkspace(cwd: string): Wor return workspace; } -function parseKitOptions(workspace: WorkspaceWithoutExplicitArgs) { +function parseKitOptions(workspace: Workspace) { const configSource = readFile(workspace, commonFilePaths.svelteConfig); const ast = parseScript(configSource); @@ -102,3 +104,28 @@ function parseKitOptions(workspace: WorkspaceWithoutExplicitArgs) { return { routesDirectory, libDirectory }; } + +let packageManager: AgentName | undefined; + +/** + * Guesses the package manager based on the detected lockfile or user-agent. + * If neither of those return valid package managers, it falls back to `npm`. + */ +export function detectPackageManager(cwd: string): AgentName { + if (packageManager) return packageManager; + + const pm = detectSync({ cwd }); + if (pm?.name) packageManager = pm.name; + + return pm?.name ?? getUserAgent() ?? 'npm'; +} + +export function getUserAgent(): AgentName | undefined { + const userAgent = process.env.npm_config_user_agent; + if (!userAgent) return undefined; + + const pmSpec = userAgent.split(' ')[0]; + const separatorPos = pmSpec.lastIndexOf('/'); + const name = pmSpec.substring(0, separatorPos) as AgentName; + return AGENTS.includes(name) ? name : undefined; +} diff --git a/packages/core/internal.ts b/packages/core/internal.ts index 7f2af1e8..079554b5 100644 --- a/packages/core/internal.ts +++ b/packages/core/internal.ts @@ -1,5 +1,10 @@ -export { installPackages } from './files/utils.ts'; +export { installPackages, getHighlighter } from './files/utils.ts'; export { createOrUpdateFiles } from './files/processors.ts'; -export { createWorkspace, type Workspace } from './files/workspace.ts'; +export { + createWorkspace, + detectPackageManager, + getUserAgent, + type Workspace +} from './files/workspace.ts'; export { TESTING } from './env.ts'; export type { Question } from './adder/options.ts'; diff --git a/packages/core/package.json b/packages/core/package.json index a7a7f9ea..dee635cc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -39,6 +39,7 @@ "@svelte-cli/clack-prompts": "workspace:*", "decircular": "^1.0.0", "dedent": "^1.5.3", + "package-manager-detector": "^0.2.2", "empathic": "^1.0.0", "picocolors": "^1.1.0" }, diff --git a/packages/core/tests/js/imports/named-import/output.ts b/packages/core/tests/js/imports/named-import/output.ts index 2df2c201..2ae493d7 100644 --- a/packages/core/tests/js/imports/named-import/output.ts +++ b/packages/core/tests/js/imports/named-import/output.ts @@ -1 +1,2 @@ +import { Handle } from "@sveltejs/kit"; import { namedOne } from "package"; \ No newline at end of file diff --git a/packages/core/tests/js/imports/named-import/run.ts b/packages/core/tests/js/imports/named-import/run.ts index e0548202..ad439355 100644 --- a/packages/core/tests/js/imports/named-import/run.ts +++ b/packages/core/tests/js/imports/named-import/run.ts @@ -3,4 +3,8 @@ import type { ScriptFileEditor } from '@svelte-cli/core'; export function run({ ast }: ScriptFileEditor): void { imports.addNamed(ast, 'package', { namedOne: 'namedOne' }, false); + + imports.addNamed(ast, '@sveltejs/kit', { Handle: 'Handle' }, false); + // adding the same import twice should not produce two imports + imports.addNamed(ast, '@sveltejs/kit', { Handle: 'Handle' }, false); } diff --git a/packages/core/tooling/js/imports.ts b/packages/core/tooling/js/imports.ts index 8a545fcf..b6cd85a6 100644 --- a/packages/core/tooling/js/imports.ts +++ b/packages/core/tooling/js/imports.ts @@ -60,7 +60,7 @@ export function addNamed( // prettier-ignore Walker.walk(ast as AstTypes.ASTNode, {}, { ImportDeclaration(node) { - if (node.source.type === 'StringLiteral' && node.source.value === importFrom && node.specifiers) { + if (node.source.value === importFrom && node.specifiers) { importDecl = node; } }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69e65538..fb246c24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -179,8 +179,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 package-manager-detector: - specifier: ^0.2.1 - version: 0.2.1 + specifier: ^0.2.2 + version: 0.2.2 picocolors: specifier: ^1.1.0 version: 1.1.0 @@ -212,6 +212,9 @@ importers: empathic: specifier: ^1.0.0 version: 1.0.0 + package-manager-detector: + specifier: ^0.2.2 + version: 0.2.2 picocolors: specifier: ^1.1.0 version: 1.1.0 @@ -1609,11 +1612,8 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - package-manager-detector@0.2.0: - resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} - - package-manager-detector@0.2.1: - resolution: {integrity: sha512-/hVW2fZvAdEas+wyKh0SnlZ2mx0NIa1+j11YaQkogEJkcMErbwchHCuo8z7lEtajZJQZ6rgZNVTWMVVd71Bjng==} + package-manager-detector@0.2.2: + resolution: {integrity: sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -2253,7 +2253,7 @@ snapshots: mri: 1.2.0 outdent: 0.5.0 p-limit: 2.3.0 - package-manager-detector: 0.2.0 + package-manager-detector: 0.2.2 picocolors: 1.1.0 resolve-from: 5.0.0 semver: 7.6.3 @@ -3583,9 +3583,7 @@ snapshots: package-json-from-dist@1.0.0: {} - package-manager-detector@0.2.0: {} - - package-manager-detector@0.2.1: {} + package-manager-detector@0.2.2: {} parent-module@1.0.1: dependencies: