From a1f1d0f8e1cb140d2e97e40a639f07ea4c359f47 Mon Sep 17 00:00:00 2001 From: Yadong Zhang Date: Sun, 5 Feb 2023 10:31:26 +0000 Subject: [PATCH 1/3] feat: added satisfies to ConfigFile [csf-tools]. --- code/lib/csf-tools/src/ConfigFile.test.ts | 85 +++++++++++++++++++++++ code/lib/csf-tools/src/ConfigFile.ts | 9 ++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/code/lib/csf-tools/src/ConfigFile.test.ts b/code/lib/csf-tools/src/ConfigFile.test.ts index 9369eb7b38cb..9404a61d825a 100644 --- a/code/lib/csf-tools/src/ConfigFile.test.ts +++ b/code/lib/csf-tools/src/ConfigFile.test.ts @@ -763,6 +763,48 @@ describe('ConfigFile', () => { expect(config.getNameFromPath(['framework'])).toEqual('foo'); }); + describe('satisfies', () => { + it(`supports string literal node`, () => { + const source = dedent` + import type { StorybookConfig } from '@storybook/react-webpack5'; + + const config = { + framework: 'foo', + } satisfies StorybookConfig + export default config; + `; + const config = loadConfig(source).parse(); + expect(config.getNameFromPath(['framework'])).toEqual('foo'); + }); + + it(`supports string literal node without variables`, () => { + const source = dedent` + import type { StorybookConfig } from '@storybook/react-webpack5'; + + export default { + framework: 'foo', + } satisfies StorybookConfig; + `; + const config = loadConfig(source).parse(); + expect(config.getNameFromPath(['framework'])).toEqual('foo'); + }); + + it(`supports object expression node with name property`, () => { + const source = dedent` + import type { StorybookConfig } from '@storybook/react-webpack5'; + + const config = { + framework: { name: 'foo', options: { bar: require('baz') } }, + "otherField": { "name": 'foo', options: { bar: require('baz') } }, + } satisfies StorybookConfig + export default config; + `; + const config = loadConfig(source).parse(); + expect(config.getNameFromPath(['framework'])).toEqual('foo'); + expect(config.getNameFromPath(['otherField'])).toEqual('foo'); + }); + }) + it(`supports object expression node with name property`, () => { const source = dedent` import type { StorybookConfig } from '@storybook/react-webpack5'; @@ -826,6 +868,49 @@ describe('ConfigFile', () => { expect(config.getNamesFromPath(['addons'])).toEqual(['foo', 'bar']); expect(config.getNamesFromPath(['otherField'])).toEqual(['foo', 'bar']); }); + + describe('satisfies', () => { + it(`supports an array with string literal and object expression with name property`, () => { + const source = dedent` + import type { StorybookConfig } from '@storybook/react-webpack5'; + + const config = { + addons: [ + 'foo', + { name: 'bar', options: {} }, + ], + "otherField": [ + "foo", + { "name": 'bar', options: {} }, + ], + } satisfies StorybookConfig + export default config; + `; + const config = loadConfig(source).parse(); + expect(config.getNamesFromPath(['addons'])).toEqual(['foo', 'bar']); + expect(config.getNamesFromPath(['otherField'])).toEqual(['foo', 'bar']); + }); + + it(`supports an array with string literal and object expression with name property without variable`, () => { + const source = dedent` + import type { StorybookConfig } from '@storybook/react-webpack5'; + + export default { + addons: [ + 'foo', + { name: 'bar', options: {} }, + ], + "otherField": [ + "foo", + { "name": 'bar', options: {} }, + ], + } satisfies StorybookConfig; + `; + const config = loadConfig(source).parse(); + expect(config.getNamesFromPath(['addons'])).toEqual(['foo', 'bar']); + expect(config.getNamesFromPath(['otherField'])).toEqual(['foo', 'bar']); + }); + }) }); it(`returns undefined when accessing a field that does not exist`, () => { diff --git a/code/lib/csf-tools/src/ConfigFile.ts b/code/lib/csf-tools/src/ConfigFile.ts index caaccf2ae7b8..a1e90466c95d 100644 --- a/code/lib/csf-tools/src/ConfigFile.ts +++ b/code/lib/csf-tools/src/ConfigFile.ts @@ -139,9 +139,12 @@ export class ConfigFile { ? _findVarInitialization(node.declaration.name, parent) : node.declaration; - if (t.isObjectExpression(decl)) { - self._exportsObject = decl; - decl.properties.forEach((p: t.ObjectProperty) => { + if (t.isObjectExpression(decl) || t.isTSSatisfiesExpression(decl)) { + const expression = t.isTSSatisfiesExpression(decl) + ? (decl.expression as Extract) + : decl; + self._exportsObject = expression; + expression.properties.forEach((p: t.ObjectProperty) => { const exportName = propKey(p); if (exportName) { let exportVal = p.value; From d684b94b9c091ff1ad21993c9d4c3151a180db5a Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Sun, 5 Feb 2023 19:35:33 +0800 Subject: [PATCH 2/3] Fix linting --- code/lib/csf-tools/src/ConfigFile.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/lib/csf-tools/src/ConfigFile.test.ts b/code/lib/csf-tools/src/ConfigFile.test.ts index 9404a61d825a..6b4db4f65c97 100644 --- a/code/lib/csf-tools/src/ConfigFile.test.ts +++ b/code/lib/csf-tools/src/ConfigFile.test.ts @@ -776,7 +776,7 @@ describe('ConfigFile', () => { const config = loadConfig(source).parse(); expect(config.getNameFromPath(['framework'])).toEqual('foo'); }); - + it(`supports string literal node without variables`, () => { const source = dedent` import type { StorybookConfig } from '@storybook/react-webpack5'; @@ -803,7 +803,7 @@ describe('ConfigFile', () => { expect(config.getNameFromPath(['framework'])).toEqual('foo'); expect(config.getNameFromPath(['otherField'])).toEqual('foo'); }); - }) + }); it(`supports object expression node with name property`, () => { const source = dedent` @@ -868,7 +868,7 @@ describe('ConfigFile', () => { expect(config.getNamesFromPath(['addons'])).toEqual(['foo', 'bar']); expect(config.getNamesFromPath(['otherField'])).toEqual(['foo', 'bar']); }); - + describe('satisfies', () => { it(`supports an array with string literal and object expression with name property`, () => { const source = dedent` @@ -910,7 +910,7 @@ describe('ConfigFile', () => { expect(config.getNamesFromPath(['addons'])).toEqual(['foo', 'bar']); expect(config.getNamesFromPath(['otherField'])).toEqual(['foo', 'bar']); }); - }) + }); }); it(`returns undefined when accessing a field that does not exist`, () => { From 7d5d6485e57dcd2439d5157e869c9b5142c91451 Mon Sep 17 00:00:00 2001 From: Yadong Zhang Date: Sun, 5 Feb 2023 12:08:55 +0000 Subject: [PATCH 3/3] fix: enhanced satisfies handle. --- code/lib/csf-tools/src/ConfigFile.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/lib/csf-tools/src/ConfigFile.ts b/code/lib/csf-tools/src/ConfigFile.ts index a1e90466c95d..552bf0d185f6 100644 --- a/code/lib/csf-tools/src/ConfigFile.ts +++ b/code/lib/csf-tools/src/ConfigFile.ts @@ -134,17 +134,17 @@ export class ConfigFile { ExportDefaultDeclaration: { enter({ node, parent }) { self.hasDefaultExport = true; - const decl = + let decl = t.isIdentifier(node.declaration) && t.isProgram(parent) ? _findVarInitialization(node.declaration.name, parent) : node.declaration; + if (t.isTSAsExpression(decl) || t.isTSSatisfiesExpression(decl)) { + decl = decl.expression; + } - if (t.isObjectExpression(decl) || t.isTSSatisfiesExpression(decl)) { - const expression = t.isTSSatisfiesExpression(decl) - ? (decl.expression as Extract) - : decl; - self._exportsObject = expression; - expression.properties.forEach((p: t.ObjectProperty) => { + if (t.isObjectExpression(decl)) { + self._exportsObject = decl; + decl.properties.forEach((p: t.ObjectProperty) => { const exportName = propKey(p); if (exportName) { let exportVal = p.value;