Skip to content

Commit

Permalink
add stateprovider directive babel plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardoperra committed Nov 4, 2024
1 parent eb02b7d commit c271d45
Show file tree
Hide file tree
Showing 9 changed files with 1,487 additions and 1,852 deletions.
18 changes: 9 additions & 9 deletions examples/counter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
"private": true,
"type": "module",
"devDependencies": {
"@types/node": "^18.11.9",
"@types/node": "^18.19.64",
"esbuild": "^0.14.54",
"postcss": "^8.4.18",
"solid-start-node": "^0.2.0",
"typescript": "^4.8.4",
"vite": "^3.1.8"
"postcss": "^8.4.47",
"solid-start-node": "^0.2.32",
"typescript": "^4.9.5",
"vite": "^3.2.11"
},
"dependencies": {
"@solidjs/meta": "^0.28.0",
"@solidjs/meta": "^0.28.7",
"@solidjs/router": "^0.6.0",
"solid-js": "^1.6.2",
"solid-start": "^0.2.0",
"solid-js": "^1.9.3",
"solid-start": "^0.2.32",
"statebuilder": "workspace:*",
"undici": "^5.11.0"
"undici": "^5.28.4"
},
"engines": {
"node": ">=16.8"
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
"url": "https://github.com/riccardoperra/statebuilder"
},
"devDependencies": {
"@changesets/cli": "^2.26.0",
"prettier": "^2.8.1",
"typescript": "^4.9.4"
"@changesets/cli": "^2.27.9",
"prettier": "^2.8.8",
"typescript": "^4.9.5"
},
"dependencies": {
"@changesets/changelog-git": "^0.1.14",
"rxjs": "^7.8.0",
"solid-js": "^1.6.7"
"rxjs": "^7.8.1",
"solid-js": "^1.9.3"
}
}
19 changes: 11 additions & 8 deletions packages/state/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,28 @@
"coverage": "vitest run --coverage"
},
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.26.0",
"@solidjs/testing-library": "^0.5.1",
"@solidjs/testing-library": "^0.5.2",
"@types/babel__core": "^7.20.5",
"@vitest/coverage-c8": "^0.26.3",
"consola": "^3.2.3",
"happy-dom": "^15.7.4",
"happy-dom": "^15.8.3",
"magicast": "^0.3.5",
"prettier": "^3.3.3",
"tsup": "^8.3.5",
"typescript": "^4.9.4",
"vite": "^5.4.9",
"typescript": "^4.9.5",
"vite": "^5.4.10",
"vite-plugin-solid": "^2.10.2",
"vite-tsconfig-paths": "^5.0.1",
"vitest": "^2.1.3"
"vite-tsconfig-paths": "^5.1.0",
"vitest": "^2.1.4"
},
"dependencies": {
"@babel/parser": "^7.26.2",
"@solid-primitives/event-bus": "^1.0.11",
"rxjs": "^7.8.0",
"solid-js": "^1.9.2"
"rxjs": "^7.8.1",
"solid-js": "^1.9.3"
},
"peerDependenciesMeta": {
"@solid-primitives/event-bus": {
Expand Down
1 change: 0 additions & 1 deletion packages/state/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
"~/*": ["./src/*"]
}
},
"include": ["./**/*.ts"],
"exclude": ["dist", "node_modules"]
}
2 changes: 1 addition & 1 deletion packages/state/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default defineConfig((options) => {
},
{
entry: ['./vite/index.ts'],
external: ['@babel/types'],
external: ['@babel/types', '@babel/core'],
outDir: './dist/vite',
format: 'esm',
platform: 'node',
Expand Down
27 changes: 20 additions & 7 deletions packages/state/vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Plugin } from 'vite';
import { ConsolaInstance, createConsola } from 'consola';
import { colors } from 'consola/utils';
import { autoKey } from './autoKey';
import { stateProviderDirective } from './stateProviderDirective';

export interface StateBuilderPluginOptions {
/**
Expand All @@ -17,7 +18,16 @@ export interface StateBuilderPluginOptions {
/**
* A set of custom primitives to be included into the plugin transform processor
*/
transformStores: string[];
transformStores?: string[];
/**
* Experimental features
*/
experimental?: {
/**
* Transform components that make use of 'use stateprovider'.
*/
transformStateProviderDirective: boolean;
};
}

export function statebuilder(options?: StateBuilderPluginOptions): Plugin[] {
Expand All @@ -37,12 +47,6 @@ export function statebuilder(options?: StateBuilderPluginOptions): Plugin[] {
enforce: 'pre',
config(userConfig, { command }) {
isDev = options?.dev ?? command === 'serve';

const plugins: Plugin[] = [];
if (options?.autoKey) {
plugins.push(autoKey({ transformStores }));
}

return {
define: {
__STATEBUILDER_DEV__: isDev,
Expand All @@ -56,6 +60,11 @@ export function statebuilder(options?: StateBuilderPluginOptions): Plugin[] {
logProperties(consola, [
['mode', isDev ? 'DEV' : 'PROD', colors.magenta],
['autoKey', options?.autoKey ?? false, colors.blue],
[
'ɵtransformStateProviderDirective',
options?.experimental?.transformStateProviderDirective ?? false,
colors.yellow,
],
]);
},
});
Expand All @@ -64,6 +73,10 @@ export function statebuilder(options?: StateBuilderPluginOptions): Plugin[] {
plugins.push(autoKey({ transformStores }));
}

if (options?.experimental?.transformStateProviderDirective) {
plugins.push(stateProviderDirective());
}

return plugins;
}

Expand Down
82 changes: 82 additions & 0 deletions packages/state/vite/internal/replaceStateProviderDirective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as t from '@babel/types';
import type * as babel from '@babel/core';

export function babelReplaceStateProviderDirective(): babel.PluginObj<any> {
return {
name: 'statebuilder:stateprovider-directive',
visitor: {
Program(path) {
let hasStateProviderDirective = false;
let hasStateProviderImport = false;

path.traverse({
ImportDeclaration(importPath) {
if (
importPath.node.source.value === 'statebuilder' &&
importPath.node.specifiers.some(
(specifier) =>
'imported' in specifier &&
'name' in specifier.imported &&
specifier.imported.name === 'StateProvider',
)
) {
hasStateProviderImport = true;
importPath.stop();
}
},
FunctionDeclaration(path) {
const bodyDirectives = path.node.body.directives || [];
const stateProviderDirective = bodyDirectives.findIndex(
(directive) => directive.value.value === 'use stateprovider',
);
if (stateProviderDirective !== -1) {
hasStateProviderDirective = true;
const originalName = path.node.id!.name;
const wrappedName = `$Original${originalName}`;
path.node.id = t.identifier(wrappedName);
path.node.body.directives = path.node.body.directives.filter(
(directive) => directive.value.value !== 'use stateprovider',
);
const wrappedComponent = t.functionDeclaration(
t.identifier(originalName),
[],
t.blockStatement([
t.returnStatement(
t.jsxElement(
t.jsxOpeningElement(t.jsxIdentifier('StateProvider'), []),
t.jsxClosingElement(t.jsxIdentifier('StateProvider')),
[
t.jsxElement(
t.jsxOpeningElement(t.jsxIdentifier(wrappedName), []),
t.jsxClosingElement(t.jsxIdentifier(wrappedName)),
[],
true,
),
],
true,
),
),
]),
);

path.insertAfter(t.exportNamedDeclaration(wrappedComponent));
}
},
});

if (hasStateProviderDirective && !hasStateProviderImport) {
const importDeclaration = t.importDeclaration(
[
t.importSpecifier(
t.identifier('StateProvider'),
t.identifier('StateProvider'),
),
],
t.stringLiteral('statebuilder'),
);
path.unshiftContainer('body', importDeclaration);
}
},
},
};
}
46 changes: 46 additions & 0 deletions packages/state/vite/stateProviderDirective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Plugin } from 'vite';
import * as magicast from 'magicast';
import { Program, traverseFast } from '@babel/types';
import * as babel from '@babel/core';
import { basename } from 'node:path';
import * as t from '@babel/types';
import { babelReplaceStateProviderDirective } from './internal/replaceStateProviderDirective';

export function stateProviderDirective(): Plugin {
return {
name: 'statebuilder:stateprovider-directive',
enforce: 'pre',
async transform(code, id, options) {
if (code.indexOf('use stateprovider') === -1) {
return;
}
const plugins: NonNullable<
NonNullable<babel.TransformOptions['parserOpts']>['plugins']
> = ['jsx'];
if (/\.[mc]?tsx?$/i.test(id)) {
plugins.push('typescript');
}

const result = await babel.transformAsync(code, {
plugins: [[babelReplaceStateProviderDirective]],
parserOpts: {
plugins,
},
filename: basename(id),
ast: false,
sourceMaps: true,
configFile: false,
babelrc: false,
sourceFileName: id,
});

if (result) {
console.log(result.code);
return {
code: result.code || '',
map: result.map,
};
}
},
};
}
Loading

0 comments on commit c271d45

Please sign in to comment.