Skip to content

Commit

Permalink
Csf Tools: Allow ConfigFile to create new import types
Browse files Browse the repository at this point in the history
  • Loading branch information
yannbf committed Jan 7, 2025
1 parent 89d1d0e commit 3e3b003
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 24 deletions.
18 changes: 18 additions & 0 deletions code/core/src/csf-tools/ConfigFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,24 @@ describe('ConfigFile', () => {
export default config;
`);
});

it(`supports setting a namespaced import`, () => {
const config = loadConfig('').parse();
config.setImport({ namespace: 'path' }, 'path');

const parsed = babelPrint(config._ast);

expect(parsed).toMatchInlineSnapshot(`import * as path from 'path';`);
});

it(`supports setting import without specifier`, () => {
const config = loadConfig('').parse();
config.setImport(null, 'path');

const parsed = babelPrint(config._ast);

expect(parsed).toMatchInlineSnapshot(`import 'path';`);
});
});

describe('setRequireImport', () => {
Expand Down
84 changes: 60 additions & 24 deletions code/core/src/csf-tools/ConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -801,16 +801,21 @@ export class ConfigFile {
*
* // import foo from 'bar';
* setImport('foo', 'bar');
*
* // import * as foo from 'bar';
* setImport({ namespace: 'foo' }, 'bar');
*
* // import 'bar';
* setImport(null, 'bar');
* ```
*
* @param importSpecifiers - The import specifiers to set. If a string is passed in, a default
* import will be set. Otherwise, an array of named imports will be set
* @param fromImport - The module to import from
*/
setImport(importSpecifier: string[] | string, fromImport: string) {
setImport(importSpecifier: string[] | string | { namespace: string } | null, fromImport: string) {
const getNewImportSpecifier = (specifier: string) =>
t.importSpecifier(t.identifier(specifier), t.identifier(specifier));

/**
* Returns true, when the given import declaration has the given import specifier
*
Expand Down Expand Up @@ -839,49 +844,80 @@ export class ConfigFile {
* hasImportSpecifier(declaration, 'foo');
* ```
*/
const hasNamespaceImportSpecifier = (declaration: t.ImportDeclaration, name: string) =>
declaration.specifiers.find(
(specifier) =>
t.isImportNamespaceSpecifier(specifier) &&
t.isIdentifier(specifier.local) &&
specifier.local.name === name
);

/** Returns true when the given import declaration has a default import specifier */
const hasDefaultImportSpecifier = (declaration: t.ImportDeclaration, name: string) =>
declaration.specifiers.find((specifier) => t.isImportDefaultSpecifier(specifier));
declaration.specifiers.find(
(specifier) =>
t.isImportDefaultSpecifier(specifier) &&
t.isIdentifier(specifier.local) &&
specifier.local.name === name
);

const importDeclaration = this._ast.program.body.find(
(node) => t.isImportDeclaration(node) && node.source.value === fromImport
) as t.ImportDeclaration | undefined;

// if the import specifier is a string, we're dealing with default imports
if (typeof importSpecifier === 'string') {
// If the import declaration with the given source exists
// Handle side-effect imports (e.g., import 'foo')
if (importSpecifier === null) {
if (!importDeclaration) {
this._ast.program.body.unshift(t.importDeclaration([], t.stringLiteral(fromImport)));
}
// Handle default imports e.g. import foo from 'bar'
} else if (typeof importSpecifier === 'string') {
if (importDeclaration) {
if (!hasDefaultImportSpecifier(importDeclaration, importSpecifier)) {
// If the import declaration hasn'types a default specifier, we add it
importDeclaration.specifiers.push(
t.importDefaultSpecifier(t.identifier(importSpecifier))
);
}
// If the import declaration with the given source doesn'types exist
} else {
// Add the import declaration to the top of the file
this._ast.program.body.unshift(
t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(importSpecifier))],
t.stringLiteral(fromImport)
)
);
}
// if the import specifier is an array, we're dealing with named imports
} else if (importDeclaration) {
importSpecifier.forEach((specifier) => {
if (!hasImportSpecifier(importDeclaration, specifier)) {
importDeclaration.specifiers.push(getNewImportSpecifier(specifier));
// Handle named imports e.g. import { foo } from 'bar'
} else if (Array.isArray(importSpecifier)) {
if (importDeclaration) {
importSpecifier.forEach((specifier) => {
if (!hasImportSpecifier(importDeclaration, specifier)) {
importDeclaration.specifiers.push(getNewImportSpecifier(specifier));
}
});
} else {
this._ast.program.body.unshift(
t.importDeclaration(
importSpecifier.map(getNewImportSpecifier),
t.stringLiteral(fromImport)
)
);
}
// Handle namespace imports e.g. import * as foo from 'bar'
} else if (importSpecifier.namespace) {
if (importDeclaration) {
if (!hasNamespaceImportSpecifier(importDeclaration, importSpecifier.namespace)) {
importDeclaration.specifiers.push(
t.importNamespaceSpecifier(t.identifier(importSpecifier.namespace))
);
}
});
} else {
this._ast.program.body.unshift(
t.importDeclaration(
importSpecifier.map((specifier) =>
t.importSpecifier(t.identifier(specifier), t.identifier(specifier))
),
t.stringLiteral(fromImport)
)
);
} else {
this._ast.program.body.unshift(
t.importDeclaration(
[t.importNamespaceSpecifier(t.identifier(importSpecifier.namespace))],
t.stringLiteral(fromImport)
)
);
}
}
}
}
Expand Down

0 comments on commit 3e3b003

Please sign in to comment.