Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pulseflow/petal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v5.0.0
Choose a base ref
...
head repository: pulseflow/petal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v5.1.0
Choose a head ref
  • 4 commits
  • 35 files changed
  • 1 contributor

Commits on Jan 7, 2025

  1. ♻️ refactor(store)!: fix types

    Signed-off-by: Pauline <git@ethanlibs.co>
    pauliesnug committed Jan 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    pauliesnug Pauline
    Copy the full SHA
    f8ba507 View commit details
  2. 🔨 chore(packageManager): update to pnpm@101

    Signed-off-by: Pauline <git@ethanlibs.co>
    pauliesnug committed Jan 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    pauliesnug Pauline
    Copy the full SHA
    c26f01b View commit details

Commits on Jan 10, 2025

  1. 🔨 chore(deps): bump

    Signed-off-by: Pauline <git@ethanlibs.co>
    pauliesnug committed Jan 10, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    pauliesnug Pauline
    Copy the full SHA
    a1b8048 View commit details
  2. chore: release v5.1.0

    pauliesnug committed Jan 10, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    pauliesnug Pauline
    Copy the full SHA
    a18a1ce View commit details
Showing with 2,572 additions and 2,031 deletions.
  1. +32 −26 package.json
  2. +1 −1 packages/create-petal/package.json
  3. +8 −6 packages/eslint-plugin/package.json
  4. +39 −36 packages/eslint-plugin/src/rules/only-export-components.ts
  5. +10 −0 packages/eslint-plugin/tests/rules/only-export-components.test.ts
  6. +47 −47 packages/eslint/package.json
  7. +10 −5 packages/eslint/src/configs/react.ts
  8. +1 −1 packages/fetch/package.json
  9. +4 −4 packages/fetch/src/lib/fetch.ts
  10. +1 −1 packages/iterator/package.json
  11. +2 −2 packages/loader/package.json
  12. +5 −3 packages/metadata/package.json
  13. +5 −3 packages/node/package.json
  14. +1 −1 packages/result/package.json
  15. +4 −4 packages/scripts/package.json
  16. +1 −1 packages/scripts/tsup.config.ts
  17. +364 −0 packages/store/README.md
  18. +5 −3 packages/store/package.json
  19. +73 −30 packages/store/src/lib/Schema.ts
  20. +3 −3 packages/store/src/lib/SchemaStore.ts
  21. +1 −1 packages/store/src/lib/types/Bit.ts
  22. +11 −0 packages/store/src/lib/types/Constant.ts
  23. +21 −0 packages/store/src/lib/types/Nullable.ts
  24. +3 −3 packages/store/src/lib/types/Snowflake.ts
  25. +2 −2 packages/store/src/lib/types/base/IType.ts
  26. +7 −0 packages/store/src/lib/types/index.ts
  27. +28 −2 packages/store/tests/Schema.test.ts
  28. +9 −0 packages/store/tests/SchemaStore.test.ts
  29. +33 −0 packages/store/tests/types.test.ts
  30. +1 −1 packages/types/package.json
  31. +2 −2 packages/typescript/package.json
  32. +3 −3 packages/utilities/package.json
  33. +59 −54 packages/utilities/src/lib/converter.ts
  34. +1,775 −1,785 pnpm-lock.yaml
  35. +1 −1 pnpm-workspace.yaml
58 changes: 32 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
@@ -2,13 +2,13 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "@flowr/root",
"type": "module",
"version": "5.0.0",
"version": "5.1.0",
"private": true,
"packageManager": "pnpm@9.15.0",
"packageManager": "pnpm@10.0.0",
"engineStrict": true,
"author": "@flowr",
"contributors": [
"Pauline <git@ethanlibs.co>"
"Pauline <me@dyn.gay>"
],
"license": "(LicenseRef-OQL-1.2 OR MIT OR Apache-2.0)",
"funding": "https://ko-fi.com/pauliesnug",
@@ -35,55 +35,61 @@
"yarn": "pnpm"
},
"scripts": {
"build": "turbo run build --concurrency=4",
"build:affected": "turbo run build --filter=...[origin/main] --concurrency=4",
"start": "turbo run start --concurrency=4",
"start:affected": "turbo run start --filter=...[origin/main] --concurrency=4",
"docs": "turbo run docs --concurrency=4",
"docs:affected": "turbo run docs --filter=...[origin/main] --concurrency=4",
"build": "pnpm turbo run build --concurrency=4",
"build:affected": "pnpm turbo run build --filter=...[origin/main] --concurrency=4",
"start": "pnpm turbo run start --concurrency=4",
"start:affected": "pnpm turbo run start --filter=...[origin/main] --concurrency=4",
"docs": "pnpm turbo run docs --concurrency=4",
"docs:affected": "pnpm turbo run docs --filter=...[origin/main] --concurrency=4",
"lint": "eslint --cache . --flag unstable_ts_config",
"lint:fix": "pnpm lint --fix",
"test": "vitest run",
"test:update": "vitest --update",
"meta:release": "bumpp -r && turbo run release --concurrency=4",
"meta:create": "turbo gen create-package --args"
"meta:release": "bumpp -r",
"turbo": "TURBO_TELEMETRY_DISABLED=1 turbo",
"meta:create": "pnpm turbo gen create-package --args"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.1",
"@arethetypeswrong/cli": "^0.17.2",
"@flowr/eslint": "workspace:^",
"@turbo/gen": "^2.3.3",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.10.1",
"@types/node": "^22.10.5",
"@vitest/coverage-v8": "^2.1.8",
"@vitest/ui": "^2.1.8",
"bumpp": "^9.9.0",
"bumpp": "^9.10.0",
"colorette": "^2.0.20",
"destr": "^2.0.3",
"esbuild": "^0.24.0",
"esbuild": "^0.24.2",
"esbuild-plugin-file-path-extensions": "^2.1.4",
"eslint": "^9.16.0",
"jiti": "^2.4.1",
"jsdom": "^25.0.1",
"eslint": "^9.17.0",
"jiti": "^2.4.2",
"jsdom": "^26.0.0",
"jsr": "^0.13.2",
"msw": "^2.6.8",
"pathe": "^1.1.2",
"msw": "^2.7.0",
"pathe": "^2.0.1",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"turbo": "^2.3.3",
"typescript": "^5.7.2",
"vite": "^6.0.3",
"typescript": "^5.7.3",
"vite": "^6.0.7",
"vitest": "^2.1.8"
},
"pnpm": {
"patchedDependencies": {
"@types/jsdom": "./packages/patches/@types__jsdom.patch"
}
},
"onlyBuiltDependencies": [
"@types/jsdom",
"esbuild",
"msw"
]
},
"resolutions": {
"@eslint-community/eslint-utils": "^4.4.1",
"@typescript-eslint/utils": "^8.17.0",
"esbuild": "^0.24.0",
"eslint": "^9.16.0",
"@typescript-eslint/utils": "^8.19.1",
"esbuild": "^0.24.2",
"eslint": "^9.17.0",
"tsx": "^4.19.2"
}
}
2 changes: 1 addition & 1 deletion packages/create-petal/package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "create-petal",
"type": "module",
"version": "5.0.0",
"version": "5.1.0",
"description": "A CLI utility to create a new Petal app",
"author": "@flowr",
"contributors": ["Pauline <git@ethanlibs.co>"],
14 changes: 8 additions & 6 deletions packages/eslint-plugin/package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "eslint-plugin-petal",
"type": "module",
"version": "5.0.0",
"version": "5.1.0",
"description": "Custom ESLint rules for the Petal specification",
"author": "@flowr",
"contributors": [
@@ -61,13 +61,15 @@
"build": "tsup"
},
"peerDependencies": {
"eslint": "^9.16.0"
"eslint": "^9.17.0"
},
"dependencies": {
"@flowr/utilities": "workspace:^"
},
"devDependencies": {
"@flowr/utilities": "workspace:^",
"@typescript-eslint/utils": "^8.17.0",
"eslint": "^9.16.0",
"eslint-vitest-rule-tester": "^0.7.1",
"@typescript-eslint/utils": "^8.19.1",
"eslint": "^9.17.0",
"eslint-vitest-rule-tester": "^1.0.0",
"jsonc-eslint-parser": "^2.4.0"
},
"publishConfig": {
75 changes: 39 additions & 36 deletions packages/eslint-plugin/src/rules/only-export-components.ts
Original file line number Diff line number Diff line change
@@ -15,12 +15,16 @@ export type Options = [{
allowConstantExport?: boolean;
checkJS?: boolean;
allowExportNames?: string[];
customHOCs?: string[];
}];

const defaultOptions: Options = [{}];
const possibleRegex = /^[A-Z][a-zA-Z0-9]*$/u;
const strictRegex = /^[A-Z][\dA-Z]*[a-z][\dA-Za-z]*$/u;
const reactHOCs = new Set(['forwardRef', 'memo']);
const defaultOptions: Options = [{
allowConstantExport: false,
checkJS: false,
allowExportNames: [],
customHOCs: [],
}];
const reactComponentNameRE = /^[A-Z][a-zA-Z0-9]*$/u;
type ToString<Type> = Type extends `${infer String}` ? String : never;
const notReactComponentExpression: Set<ToString<TSESTree.Expression['type']>> = new Set([
'ArrayExpression',
@@ -49,20 +53,30 @@ export default createEslintRule<Options, MessageIds>({

return {
Program: (program) => {
const ruleContext = {
hasExports: false,
mayHaveReactExport: false,
reactIsInScope: false,
};
const ruleContext = { hasExports: false, hasReactExport: false, reactIsInScope: false };
const localComponents: TSESTree.Identifier[] = [];
const nonComponentExports: Array<TSESTree.BindingName | TSESTree.StringLiteral> = [];
const allowExportNamesSet = options.allowExportNames ? new Set(options.allowExportNames) : undefined;
const allowExportNames = new Set(options.allowExportNames);
const reactHOCs = new Set(['forwardRef', 'memo', ...options.customHOCs!]);
const reactContextExports: TSESTree.Identifier[] = [];

const canBeReactFunctionComponent = (init: TSESTree.VariableDeclaratorMaybeInit['init']): boolean => {
if (!init)
return false;

if (init.type === 'ArrowFunctionExpression')
return true;

if (init.type === 'CallExpression' && init.callee.type === 'Identifier')
return reactHOCs.has(init.callee.name);

return false;
};

const handleLocalIdentifier = (id: TSESTree.BindingName): void => {
if (id.type !== 'Identifier')
return;
if (possibleRegex.test(id.name))
if (reactComponentNameRE.test(id.name))
localComponents.push(id);
};

@@ -72,16 +86,16 @@ export default createEslintRule<Options, MessageIds>({
return;
}

if (allowExportNamesSet?.has(id.name))
if (allowExportNames.has(id.name))
return;

// Literal: 1, 'foo', UnaryExpression: -1, TemplateLiteral: `Some ${template}`, BinaryExpression: 24 * 60.
if (options.allowConstantExport && init && ['BinaryExpression', 'Literal', 'TemplateLiteral', 'UnaryExpression'].includes(init.type))
return;

if (isFn) {
if (possibleRegex.test(id.name))
ruleContext.mayHaveReactExport = true;
if (reactComponentNameRE.test(id.name))
ruleContext.hasReactExport = true;
else nonComponentExports.push(id);
}
else {
@@ -100,9 +114,9 @@ export default createEslintRule<Options, MessageIds>({
nonComponentExports.push(id);
return;
}
if (!ruleContext.mayHaveReactExport && possibleRegex.test(id.name))
ruleContext.mayHaveReactExport = true;
if (!strictRegex.test(id.name))
if (reactComponentNameRE.test(id.name))
ruleContext.hasReactExport = true;
else
nonComponentExports.push(id);
}
};
@@ -116,17 +130,17 @@ export default createEslintRule<Options, MessageIds>({
else handleExportIdentifier(node.id, true);
else if (node.type === 'CallExpression')
if (node.callee.type === 'CallExpression' && node.callee.callee.type === 'Identifier' && node.callee.callee.name === 'connect')
ruleContext.mayHaveReactExport = true;
ruleContext.hasReactExport = true;
else if (node.callee.type !== 'Identifier')
if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier' && reactHOCs.has(node.callee.property.name))
ruleContext.mayHaveReactExport = true;
ruleContext.hasReactExport = true;
else context.report({ messageId: 'anonymousExport', node });
else if (!reactHOCs.has(node.callee.name))
context.report({ messageId: 'anonymousExport', node });
else if (node.arguments[0].type === 'FunctionExpression' && node.arguments[0].id)
handleExportIdentifier(node.arguments[0].id, true);
else if (node.arguments[0]?.type === 'Identifier')
ruleContext.mayHaveReactExport = true;
ruleContext.hasReactExport = true;
else context.report({ messageId: 'anonymousExport', node });
else if (node.type === 'TSEnumDeclaration')
nonComponentExports.push(node.id);
@@ -141,11 +155,10 @@ export default createEslintRule<Options, MessageIds>({
}
else if (node.type === 'ExportDefaultDeclaration') {
ruleContext.hasExports = true;
const declaration
= node.declaration.type === 'TSAsExpression'
const declaration = node.declaration.type === 'TSAsExpression'
|| node.declaration.type === 'TSSatisfiesExpression'
? node.declaration.expression
: node.declaration;
? node.declaration.expression
: node.declaration;
if (
declaration.type === 'VariableDeclaration'
|| declaration.type === 'FunctionDeclaration'
@@ -181,7 +194,7 @@ export default createEslintRule<Options, MessageIds>({
return;

if (ruleContext.hasExports)
if (ruleContext.mayHaveReactExport) {
if (ruleContext.hasReactExport) {
nonComponentExports.forEach(node => context.report({ messageId: 'namedExport', node }));
reactContextExports.forEach(node => context.report({ messageId: 'reactContext', node }));
}
@@ -214,21 +227,11 @@ export default createEslintRule<Options, MessageIds>({
allowConstantExport: { type: 'boolean' },
allowExportNames: { items: { type: 'string' }, type: 'array' },
checkJS: { type: 'boolean' },
customHOCs: { type: 'array', items: { type: 'string' } },
} satisfies Readonly<Record<keyof Options[0], JSONSchema4>>,
type: 'object',
}],
type: 'problem',
},
name: RULE_NAME,
});

function canBeReactFunctionComponent(init: TSESTree.Expression | null): boolean {
if (!init)
return false;
if (init.type === 'ArrowFunctionExpression')
return true;
if (init.type === 'CallExpression' && init.callee.type === 'Identifier')
return ['forwardRef', 'memo'].includes(init.callee.name);

return false;
}
10 changes: 10 additions & 0 deletions packages/eslint-plugin/tests/rules/only-export-components.test.ts
Original file line number Diff line number Diff line change
@@ -185,6 +185,11 @@ const valids: ValidTestCase[] = [
name: 'Only React context',
code: 'export const MyContext = createContext(\'test\');',
},
{
name: 'Custom HOCs like mobx observer',
code: 'const MyComponent = () => {}; export default observer(MyComponent);',
options: [{ customHOCs: ['observer'] }],
},
];

const invalid: InvalidTestCase[] = [
@@ -286,6 +291,11 @@ const invalid: InvalidTestCase[] = [
code: 'export const MyComponent = () => {}; export const MyContext = React.createContext(\'test\');',
errors: [{ messageId: 'reactContext' }],
},
{
name: 'should be invalid when custom HOC is used without adding it to the rule configuration',
code: 'const MyComponent = () => {}; export default observer(MyComponent);',
errors: [{ messageId: 'localComponents' }, { messageId: 'anonymousExport' }],
},
];

run({
Loading