Repository: lxsmnsyc/solid-labels Branch: main Commit: 56eecd581df1 Files: 88 Total size: 203.3 KB Directory structure: gitextract_zomu0hq3/ ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── .npmrc ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── biome.json ├── docs/ │ ├── comments.md │ ├── ctf.md │ ├── labels.md │ └── namespace.md ├── examples/ │ ├── comments/ │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ └── main.tsx │ │ ├── tsconfig.json │ │ └── vite.config.js │ ├── ctf/ │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ └── main.tsx │ │ ├── tsconfig.json │ │ └── vite.config.js │ └── labels/ │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.tsx │ │ └── main.tsx │ ├── tsconfig.json │ └── vite.config.js ├── lerna.json ├── package.json ├── packages/ │ ├── core/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel/ │ │ │ ├── components.ts │ │ │ ├── constants.ts │ │ │ ├── core/ │ │ │ │ ├── accessor-variable.ts │ │ │ │ ├── assert.ts │ │ │ │ ├── deferred-variable.ts │ │ │ │ ├── deref-memo-variable.ts │ │ │ │ ├── deref-memo.ts │ │ │ │ ├── deref-signal-variable.ts │ │ │ │ ├── deref-signal.ts │ │ │ │ ├── destructure-variable.ts │ │ │ │ ├── errors.ts │ │ │ │ ├── generate-unique-name.ts │ │ │ │ ├── get-import-identifier.ts │ │ │ │ ├── is-awaited.ts │ │ │ │ ├── is-in-typescript.ts │ │ │ │ ├── is-static.ts │ │ │ │ ├── is-yielded.ts │ │ │ │ ├── memo-variable.ts │ │ │ │ ├── proto.ts │ │ │ │ ├── signal-variable.ts │ │ │ │ ├── types.ts │ │ │ │ └── unwrap-node.ts │ │ │ ├── index.ts │ │ │ ├── transform-comment.ts │ │ │ ├── transform-ctf.ts │ │ │ └── transform-label.ts │ │ ├── example.js │ │ ├── package.json │ │ ├── pridepack.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── test/ │ │ │ ├── __snapshots__/ │ │ │ │ └── ctf.test.ts.snap │ │ │ └── ctf.test.ts │ │ └── tsconfig.json │ ├── rollup/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── pridepack.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── unplugin/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── pridepack.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ └── vite/ │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── pridepack.json │ ├── src/ │ │ └── index.ts │ └── tsconfig.json └── pnpm-workspace.yaml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: - push jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: 8 run_install: true - uses: actions/setup-node@v4 with: node-version: 20 cache: "pnpm" - name: Clean run: pnpm recursive run clean env: CI: true - name: Build run: pnpm recursive run build env: CI: true - name: Begin Tests run: pnpm recursive run test env: CI: true ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # parcel-bundler cache (https://parceljs.org/) .cache # next.js build output .next # nuxt.js build output .nuxt # vuepress build output .vuepress/dist # Serverless directories .serverless # FuseBox cache .fusebox/ dist .DS_Store ================================================ FILE: .npmrc ================================================ strict-peer-dependencies=false prefer-workspace-packages=true link-workspace-packages=true ================================================ FILE: .vscode/settings.json ================================================ { "editor.defaultFormatter": "biomejs.biome", "[typescript]": { "editor.defaultFormatter": "biomejs.biome" }, "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" }, "[javascript]": { "editor.defaultFormatter": "biomejs.biome" }, "[json]": { "editor.defaultFormatter": "biomejs.biome" } } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2025 Alexis Munsayac Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # `solid-labels` [![NPM](https://img.shields.io/npm/v/solid-labels.svg)](https://www.npmjs.com/package/solid-labels) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript)

Example

## Install ```bash npm install solid-labels ``` ```bash yarn add solid-labels ``` ```bash pnpm add solid-labels ``` ## Features - 🏷 Labels: Turn labels into SolidJS utility calls! - 💬 Comments: Turn comments into SolidJS utility calls, too! - ⏱ Compile-time Functions: Use functions that are evaluated during compile-time! - 📦 Auto Imports: No need to import SolidJS utilities, explicitly! - 🤝 JS and TS Friendly! ## Usage - [Labels](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/labels.md) [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/solid-labels/tree/main/examples/labels) - [Comments](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/comments.md) [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/solid-labels/tree/main/examples/comments) - [Compile-Time Functions](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/ctf.md) [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/solid-labels/tree/main/examples/ctf) - [Solid Namespace](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/namespace.md) ### Typescript `.d.ts` ```ts /// ``` ### Babel `.babelrc` ```json { "plugins": [ ["solid-labels/babel", { "dev": false }] ] } ``` [!INFO]: You don't have to use this if you're using Vite or Rollup plugins ## Integrations - [Vite](https://github.com/lxsmnsyc/solid-labels/tree/main/packages/vite) - [Rollup](https://github.com/lxsmnsyc/solid-labels/tree/main/packages/rollup) - [Unplugin](https://github.com/lxsmnsyc/solid-labels/tree/main/packages/unplugin) ### Disabling features You can disable some features by passing `disabled` option to the plugin options. ```js { disabled: { labels: { signal: true, }, pragma: { '@signal': true, }, ctf: { $signal: true, }, } } ``` ## Limitations - Detecting shadowed identifier for `signal` and `memo`. ## Sponsors ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) ## License MIT © [lxsmnsyc](https://github.com/lxsmnsyc) ================================================ FILE: biome.json ================================================ { "$schema": "https://unpkg.com/@biomejs/biome/configuration_schema.json", "files": { "ignore": ["node_modules/**/*"] }, "vcs": { "useIgnoreFile": true }, "linter": { "enabled": true, "ignore": ["node_modules/**/*"], "rules": { "recommended": true, "a11y": { "noAriaHiddenOnFocusable": "off", "useIframeTitle": "warn", "useKeyWithClickEvents": "warn", "useKeyWithMouseEvents": "warn" }, "complexity": { "noForEach": "error", "noVoid": "off", "useOptionalChain": "warn", "useSimplifiedLogicExpression": "error" }, "correctness": { "noConstantMathMinMaxClamp": "error", "noInvalidNewBuiltin": "error", "noNewSymbol": "error", "noNodejsModules": "off", "noUndeclaredDependencies": "off", "noUndeclaredVariables": "error", "noUnusedFunctionParameters": "error", "noUnusedImports": "error", "noUnusedPrivateClassMembers": "error", "noUnusedVariables": "error", "useArrayLiterals": "error" }, "performance": { "noAccumulatingSpread": "error", "useTopLevelRegex": "error" }, "security": { "noGlobalEval": "off" }, "style": { "noArguments": "error", "noImplicitBoolean": "error", "noInferrableTypes": "error", "noNamespace": "error", "noNegationElse": "error", "noRestrictedGlobals": "error", "noShoutyConstants": "error", "noUnusedTemplateLiteral": "error", "noUselessElse": "error", "noVar": "error", "noYodaExpression": "error", "useAsConstAssertion": "error", "useBlockStatements": "error", "useCollapsedElseIf": "error", "useConsistentArrayType": "error", "useConsistentBuiltinInstantiation": "error", "useConst": "error", "useDefaultParameterLast": "error", "useEnumInitializers": "error", "useExponentiationOperator": "error", "useExportType": "error", "useFragmentSyntax": "error", "useForOf": "warn", "useImportType": "error", "useLiteralEnumMembers": "error", "useNodejsImportProtocol": "warn", "useNumberNamespace": "error", "useNumericLiterals": "error", "useSelfClosingElements": "error", "useShorthandArrayType": "error", "useShorthandAssign": "error", "useShorthandFunctionType": "warn", "useSingleCaseStatement": "error", "useSingleVarDeclarator": "error", "useTemplate": "off", "useWhile": "error" }, "suspicious": { "noConsoleLog": "warn", "noConstEnum": "off", "noDebugger": "off", "noEmptyBlockStatements": "error", "noExplicitAny": "warn", "noImplicitAnyLet": "off", "noMisrefactoredShorthandAssign": "off", "noSelfCompare": "off", "noSparseArray": "off", "noThenProperty": "warn", "useAwait": "error", "useErrorMessage": "error" } } }, "formatter": { "enabled": true, "ignore": ["node_modules/**/*"], "formatWithErrors": false, "indentWidth": 2, "indentStyle": "space", "lineEnding": "lf", "lineWidth": 80 }, "organizeImports": { "enabled": true, "ignore": ["node_modules/**/*"] }, "javascript": { "formatter": { "enabled": true, "arrowParentheses": "asNeeded", "bracketSameLine": false, "bracketSpacing": true, "indentWidth": 2, "indentStyle": "space", "jsxQuoteStyle": "double", "lineEnding": "lf", "lineWidth": 80, "quoteProperties": "asNeeded", "quoteStyle": "single", "semicolons": "always", "trailingCommas": "all" }, "globals": [], "parser": { "unsafeParameterDecoratorsEnabled": true } }, "json": { "formatter": { "enabled": true, "indentWidth": 2, "indentStyle": "space", "lineEnding": "lf", "lineWidth": 80 }, "parser": { "allowComments": false, "allowTrailingCommas": false } } } ================================================ FILE: docs/comments.md ================================================ # Comments ## Utilities ### `@signal` Transforms into `createSignal`: ```js function Counter() { // @signal let x = 0; function increment() { x += 1; } return () => x; } ``` becomes ```js import { createSignal as _createSignal } from "solid-js"; function Counter() { const [_x, _setx] = _createSignal(0); function increment() { _setx(_current => _current += 1); } return () => _x(); } ``` Chained variable declaration is also supported. ```js // @signal let x = 0, y = 0, z = 0; ``` ### `@memo` Transforms into `createMemo`: ```js function Counter() { // @signal let x = 0; // @memo const message = `Count: ${x}`; return () => message; } ``` becomes ```js import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; function Counter() { // let [_x, _setx] = _createSignal(0, { name: "x" }); // const _message = _createMemo(() => `Count: ${_x()}`, undefined, { name: "message" }); return () => _message(); } ``` Chained variable declaration is also supported. ```js // @memo const y = x + 10, z = y / 10; ``` ### `@effect`, `@computed` and `@renderEffect` Transforms into `createEffect`, `createComputed` and `createRenderEffect`, respectively. ```js function Counter() { // @signal let x = 0; /* @effect */ { console.log('Count', x); } /* @computed */ { console.log('Count', x); } /* @renderEffect */ { console.log('Count', x); } } ``` into ```js import { createRenderEffect as _createRenderEffect } from "solid-js"; import { createComputed as _createComputed } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; function Counter() { const [_x, _setx] = _createSignal(0); _createEffect(() => { console.log('Count', _x()); }); _createComputed(() => { console.log('Count', _x()); }); _createRenderEffect(() => { console.log('Count', _x()); }); } ``` You may use an arrow function instead of a block statement to accepts the previously returned value. `@effect`, `@computed` and `@renderEffect` can also be named: ```js // @signal let x = 0; /* @effect Effect Log */ { console.log('Count', x); } /* @computed Computed Log */ { console.log('Count', x); } /* @renderEffect Render Effect Log */ { console.log('Count', x); } ``` ```js import { createRenderEffect as _createRenderEffect } from "solid-js"; import { createComputed as _createComputed } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; // let [_x, _setx] = _createSignal(0); /**/ _createEffect(() => { console.log('Count', _x()); }, undefined, { name: "Effect Log" }); /**/ _createComputed(() => { console.log('Count', _x()); }, undefined, { name: "Computed Log" }); /**/ _createRenderEffect(() => { console.log('Count', _x()); }, undefined, { name: "Render Effect Log" }); ``` ### `@mount`, `@cleanup` and `@error` Transforms into `onMount`, `onCleanup` and `onError`. ```js function Counter() { /* @mount */ { console.log('Mounted!'); } /* @cleanup */ { console.log('Cleaned!'); } /* @error */ { console.log('Something went wrong.'); } } ``` into ```js import { onError as _onError } from "solid-js"; import { onCleanup as _onCleanup } from "solid-js"; import { onMount as _onMount } from "solid-js"; function Counter() { _onMount(() => { console.log('Mounted!'); }); _onCleanup(() => { console.log('Cleaned!'); }); _onError(() => { console.log('Something went wrong.'); }); } ``` You may also use an arrow function. For `onError`, an arrow function with a parameter may be used to receive the error object. ### `@untrack` and `@batch` Transforms into `untrack` and `batch`. ```js function Counter() { /* @batch */ { console.log('This is batched!'); } /* @untrack */ { console.log('This is untracked!'); } } ``` into ```js import { untrack as _untrack } from "solid-js"; import { batch as _batch } from "solid-js"; function Counter() { _batch(() => { console.log('This is batched!'); }); _untrack(() => { console.log('This is untracked!'); }); } ``` ### `@root` Transforms into `createRoot` ```js /* @root */ { element = renderComponent(MyComponent); } ``` into ```js import { createRoot as _createRoot } from "solid-js"; _createRoot(() => { element = renderComponent(MyComponent); }); ``` You can also pass an arrow function instead of a block to receive the `dispose` callback. ### `@children` Compiles to `children`. ```js // @children const nodes = props.children; ``` ```js import { children as _children } from "solid-js"; // const _nodes = _children(() => props.children); ``` ### `@deferred` Compiles to `createDeferred`. ```js // @signal let searchInput = ''; // @deferred let deferredSearchInput = searchInput; effect: { fetchResults(deferredSearchInput); } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { createDeferred as _createDeferred } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; // let [_searchInput, _setsearchInput] = _createSignal('', { name: "searchInput" }); // let _deferredSearchInput = _createDeferred(() => _searchInput(), { name: "deferredSearchInput" }); _createEffect(() => { fetchResults(_deferredSearchInput()); }); ``` ### `@transition` Compiles to `startTransition`. Arrow function can be provided instead of blocks. ```js // @signal let data; /* @transition */ { data = fetchData(); } ``` ```js import { startTransition as _startTransition } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; // let [_data, _setdata] = _createSignal(undefined, { name: "data" }); /**/ _startTransition(() => { _setdata(() => fetchData()); }); ``` ### `@destructure` Destructures an object while retaining reactivity. This partially compiles to `splitProps` if a rest expression is detected. `@destructure` also supports nested destructures. Does not support default assignment. ```js // @destructure let { a: { b, c }, b: { d, e }, ...f } = x; effect: { console.log(b, c); } effect: { console.log(d, e); } effect: { console.log(f); } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { splitProps as _splitProps } from "solid-js"; // const _prop = () => x.a, _prop2 = () => _prop().b, _prop3 = () => _prop().c, _other2 = _splitProps(_prop(), ["b", "c"])[1], _prop4 = () => x.b, _prop5 = () => _prop4().d, _prop6 = () => _prop4().e, _other3 = _splitProps(_prop4(), ["d", "e"])[1], _other = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log(_prop2(), _prop3()); }); _createEffect(() => { console.log(_prop5(), _prop6()); }); _createEffect(() => { console.log(_other); }); ``` ## Tooling ### ESLint ```json { "rules": { "no-lone-blocks": "off" }, } ``` ================================================ FILE: docs/ctf.md ================================================ # Compile-Time Functions ## Reactivity ### `$signal` ```ts function $signal(): T | undefined; function $signal( value: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; internal?: boolean, } ): T; ``` Compiles into `createSignal`. ```js let count = $signal(0); ``` becomes ```js import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); ``` `$signal` is only allowed to be used on variable declarations, otherwise it raises a compile-time error. ### `$memo` ```ts function $memo( value: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string } ): T; function $memo( value: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string } ): T; ``` Compiles into `createMemo`. ```js let count = $signal(0); const message = $memo(`Count: ${count}`); ``` becomes ```js import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const _message = _createMemo(() => `Count: ${_count()}`, undefined, { name: "message" }); ``` `$memo` is only allowed to be used on variable declarations, otherwise it raises a compile-time error. ### `$` ```ts function $(value: T): T; ``` Compiles into `createMemo` when used in variable declarations, otherwise it compiles to `createEffect` when used in single-line statements. If used for other expressions, it raises a compile-time error. ```js let count = $signal(0); const message = $(`Count: ${count}`); $(console.log(message)); ``` compiles into ```js import { createEffect as _createEffect } from "solid-js"; import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const _message = _createMemo(() => `Count: ${_count()}`, undefined, { name: "message" }); _createEffect(() => console.log(_message())); ``` ### `$untrack` and `$batch` ```ts function $untrack(value: T): T; function $batch(value: T): T; ``` Compiles into `untrack` and `batch`, respectively. Unlike `untrack` and `batch`, `$untrack` and `$batch` doesn't have to provide an arrow function. ```js let count = $signal(0); effect: { console.log($untrack(count)); $batch(updateData(count)); } ``` compiles into ```js import { batch as _batch } from "solid-js"; import { untrack as _untrack } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); _createEffect(() => { console.log(_untrack(() => _count())); _batch(() => updateData(_count())); }); ``` ### `$effect`, `$computed` and `$renderEffect` ```ts function $effect(fn: (v?: T) => T | undefined): void; function $effect(fn: (v: T) => T, value: T, options?: { name?: string }): void; function $computed(fn: (v?: T) => T | undefined): void; function $computed(fn: (v: T) => T, value: T, options?: { name?: string }): void; function $renderEffect(fn: (v?: T) => T | undefined): void; function $renderEffect(fn: (v: T) => T, value: T, options?: { name?: string }): void; ``` Compiles to `createEffect`, `createComputed` and `createRenderEffect`, respectively. ```js let x = $signal(0); $effect(() => { console.log('Count', x); }); $computed(() => { console.log('Count', x); }); $renderEffect(() => { console.log('Count', x); }); ``` ```js import { createRenderEffect as _createRenderEffect } from "solid-js"; import { createComputed as _createComputed } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_x, _setx] = _createSignal(0, { name: "x" }); _createEffect(() => { console.log('Count', _x()); }); _createComputed(() => { console.log('Count', _x()); }); _createRenderEffect(() => { console.log('Count', _x()); }); ``` ### `$root` ```ts function $root(value: T): T; function $root(cb: (dispose: () => void) => T): T; ``` Compiles to `createRoot`. Can be called inline. Arrow function is automatically injected if not provided. ```js const cleanup = $root((dispose) => { renderApp(); return dispose; }); $root(renderApp()); ``` ```js import { createRoot as _createRoot } from "solid-js"; const cleanup = _createRoot(dispose => { renderApp(); return dispose; }); _createRoot(() => renderApp()); ``` ### `$selector` ```ts function $selector( source: T, fn?: (a: U, b: T) => boolean, options?: { name?: string } ): (key: U) => boolean; ``` Compiles to `createSelector`. Automatically injects arrow function to `source`. ```js let selectedID = $signal(); let isSelected = $selector(selectedID); effect: { if (isSelected(userID)) { selectUser(userID); } } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { createSelector as _createSelector } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_selectedID, _setselectedID] = _createSignal(undefined, { name: "selectedID" }); let isSelected = _createSelector(() => _selectedID()); _createEffect(() => { if (isSelected(userID)) { selectUser(userID); } }); ``` ### `$on` ```ts function $on( deps: T, fn: (input: T, prevInput: T, prevValue?: U) => U, options?: { defer?: boolean } ): (prevValue?: U) => U; ``` Compiles to `on`. Arrow function is automatically injected to `deps`. ```js let selectedID = $signal(); effect: $on(selectedID, (value) => { if (userID === value) { selectUser(userID); } }) ``` ```js import { on as _on } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_selectedID, _setselectedID] = _createSignal(undefined, { name: "selectedID" }); _createEffect(_on(() => _selectedID(), value => { if (userID === value) { selectUser(userID); } })); ``` ### `$deferred` ```ts function $deferred( source: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; timeoutMs?: number; }, ): T; ``` Compiles to `createDeferred`. Automatically injects arrow function to `source`. Automatically treats variable reads as accessor calls like `$memo`. Can only be called in variable declarations. ```js let searchInput = $signal(''); let deferredSearchInput = $deferred(searchInput); effect: { fetchResults(deferredSearchInput); } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { createDeferred as _createDeferred } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_searchInput, _setsearchInput] = _createSignal('', { name: "searchInput" }); let _deferredSearchInput = _createDeferred(() => _searchInput(), { name: "deferredSearchInput" }); _createEffect(() => { fetchResults(_deferredSearchInput()); }); ``` ### `$resource` This compile as an auto-import alias for `createResource`. ### `$reaction` This CTF is a compile-time alias for the `createReaction` utility that was added in 1.3. ## Lifecycles The following CTFs are compile-time aliases: - `$cleanup` -> `onCleanup` - `$mount` -> `onMount` - `$error` -> `onError` ## Observables ### `$observable` ```ts function $observable(value: T): Observable; ``` Compiles to `observable`. Automatically injects arrow function to `value`. ```js let count = $signal(0); const counter$ = $observable(count); ``` ```js import { observable as _observable } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const counter$ = _observable(() => _count()); ``` ### `$from` ```ts function $from(observable: Observable): T; function $from(produce: ((setter: Setter) => () => void)): T; ``` Compiles to `from`. Automatically treats variable reads as accessor calls like `$memo`. ```js let count = $signal(0); const counter$ = $observable(count); const counter = $from(counter$); effect: { console.log('Count:', counter); } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { from as _from } from "solid-js"; import { observable as _observable } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const counter$ = _observable(() => _count()); const _counter = _from(counter$); _createEffect(() => { console.log('Count:', _counter()); }); ``` ## Objects ### `$destructure` ```ts function $destructure(value: T): T; ``` Destructures an object while retaining reactivity. This partially compiles to `splitProps` if a rest expression is detected. `$destructure` also supports nested destructures. Does not support default assignment. ```js let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log(b, c); } effect: { console.log(d, e); } effect: { console.log(f); } ``` ```js import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let _prop = () => x.a, _prop2 = () => _prop().b, _prop3 = () => _prop().c, _other2 = _splitProps(_prop(), ["b", "c"])[1], _prop4 = () => x.b, _def = defaultD, _prop5 = () => { const _value = _prop4().d; return _value === undefined ? _def : _value; }, _def2 = defaultE, _prop6 = () => { const _value2 = _prop4().e; return _value2 === undefined ? _def2 : _value2; }, _other3 = _splitProps(_prop4(), ["d", "e"])[1], _other = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log(_prop2(), _prop3()); }); _createEffect(() => { console.log(_prop5(), _prop6()); }); _createEffect(() => { console.log(_other); }); ``` ### `$merge` ```ts function $merge(...args: T): MergeProps; ``` Merges one or more objects while retaining reactivity. Compiles to `mergeProps`. ```js const mergedObject = $merge(a, b, c, d); ``` ```js import { mergeProps as _mergeProps } from "solid-js"; const mergedObject = _mergeProps(a, b, c, d); ``` ### `$component` A compile-time function used to wrap components and implicitly allow prop destructuring, much like `$destructure`. ```js $component(({ [x]: { y, ...z } = { y: 10 }, ...a }) => ( <> {y} {z} {a} )) ``` ```js import { mergeProps as _mergeProps } from "solid-js"; import { splitProps as _splitProps } from "solid-js"; _props => { const _def = { y: 10 }, _prop = () => { const _value = _props[x]; return _value === undefined ? _def : _value; }, _prop2 = () => _prop().y, _other2 = _splitProps(_mergeProps(_prop(), _def), ["y"])[1], _other = _splitProps(_props, [x])[1]; return <> {_prop2()} {_other2} {_other} ; }; ``` ## Arrays ### `$mapArray` ```ts function $mapArray( arr: readonly T[] | undefined | null | false, mapFn: (v: T, i: Accessor) => U, options?: { fallback?: Accessor; }, ): U[]; ``` Compiles to `mapArray`. Automatically treats variable reads as accessor calls like `$memo`. Automatically injects arrow function to `arr`. ```js let list = $signal([]); const squaredList = $mapArray(list, (value) => value ** 2); effect: { console.log('1st value', squaredList[0]); } list = ['Hello']; ``` ```js import { createEffect as _createEffect } from "solid-js"; import { mapArray as _mapArray } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_list, _setlist] = _createSignal([], { name: "list" }); const _squaredList = _mapArray(() => _list(), value => value ** 2); _createEffect(() => { console.log('1st value', _squaredList()[0]); }); _setlist(() => ['Hello']); ``` ### `$indexArray` ```ts function $indexArray( arr: readonly T[] | undefined | null | false, mapFn: (v: Accessor, i: number) => U, options?: { fallback?: Accessor; }, ): U[]; ``` Compiles to `mapArray`. Automatically treats variable reads as accessor calls like `$memo`. Automatically injects arrow function to `arr`. ```js let list = $signal([]); const squaredList = $indexArray(list, (value) => value() ** 2); effect: { console.log('1st value', squaredList[0]); } list = ['Hello']; ``` ```js import { createEffect as _createEffect } from "solid-js"; import { indexArray as _indexArray } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_list, _setlist] = _createSignal([], { name: "list" }); const _squaredList = _indexArray(() => _list(), value => value() ** 2); _createEffect(() => { console.log('1st value', _squaredList()[0]); }); _setlist(() => ['Hello']); ``` ## Component APIs ### `$uid` ```ts function $uid(): string; ``` Compiles to `createUniqueId` (Only available for Solid 1.1.0+) ```js const elementID = $uid(); ``` ```ts import { createUniqueId as _createUniqueId } from "solid-js"; const elementID = _createUniqueId(); ``` ### `$lazy` ```ts function $lazy>(fn: Promise<{ default: T }>): T & { preload: () => void; }; ``` Compiles into `lazy`. Arrow function is automatically injected. ```js const LazyComponent = $lazy(import('./LazyComponent')); ``` ```js import { lazy as _lazy } from "solid-js"; const LazyComponent = _lazy(() => import('./LazyComponent')); ``` ### `$children` ```ts function $children(value: JSX.Element): JSX.Element; ``` Compiles to `children`. Arrow function is automatically injected. ```js const nodes = $children(props.children); ``` ```js import { children as _children } from "solid-js"; const _nodes = _children(() => props.children); ``` ### Context ```ts function $createContext(): Context; function $createContext(defaultValue: T): Context; function $useContext(context: Context): T; ``` Compiles to `createContext` and `useContext`. ```js const CounterContext = $createContext(0); const value = $useContext(CounterContext); ``` ```js import { useContext as _useContext } from "solid-js"; import { createContext as _createContext } from "solid-js"; const CounterContext = _createContext(0); const value = _useContext(CounterContext); ``` ### Transition ```ts function $startTransition(fn: () => unknown): Promise function $useTransition(): Transition ``` ### Owner ```ts function $owner(): Owner | null function $runWithOwner(o: Owner, fn: () => T): T ``` Compiles to `useTransition` and `startTransition`. ## Refs and Derefs Ref and Deref allows opting in and out of the labels syntax. This is useful for scenarios where we want to return the actual signal tuple instead of the signal value or when we have a third-party utility that we want to opt-into ```ts function $derefSignal(value: [Accessor, Setter]): T; function $refSignal(value: T): [Accessor, Setter]; function $derefMemo(value: Accessor): T; function $refMemo(value: T): Accessor; ``` ### `$refSignal` `$refSignal` returns the signal tuple of a given signal variable. ```js let count = $signal(0); const [value, setValue] = $refSignal(count); ``` compiles into ```js import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const [value, setValue] = [_count, _setcount]; ``` `$refSignal` also works with `signal:` label and `@signal` comment. ### `$refMemo` `$refMemo` returns the accessor of the given memo variable. This is similar to `() => memoVariable`. ```js let count = $signal(0); const message = $memo(`Count: ${count}`); const getMessage = $refMemo(message); getMessage(); ``` compiles into ```js import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const _message = _createMemo(() => `Count: ${_count()}`, undefined, { name: "message" }); const getMessage = _message; getMessage(); ``` `$refMemo` also works with `memo:` label and `@memo` comment. ### `$get` and `$set` ```ts function $get(value: T): Accessor; function $set(value: T): Setter; ``` `$get` returns the accessor for a given signal or memo variable. ```js let count = $signal(0); const message = $memo(`Count: ${count}`); const getMessage = $get(message); const getCount = $get(count); getMessage(); getCount(); ``` compiles into ```js import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const _message = _createMemo(() => `Count: ${_count()}`, undefined, { name: "message" }); const getMessage = _message; const getCount = _count; getMessage(); getCount(); ``` `$get` works with `signal:` and `memo:` labels as well as `@signal` and `@memo` comments. ### `$set` `$set` returns the setter of the given signal variable. ```js let count = $signal(0); const setCount = $set(count); setCount(10); ``` compiles into ```js import { createSignal as _createSignal } from "solid-js"; let [_count, _setcount] = _createSignal(0, { name: "count" }); const setCount = _setcount; setCount(10); ``` `$set` also works with `signal:` label and `@signal` comment. ### `$derefSignal` `$derefSignal` converts any kind of signal tuple into a signal variable. ```js let count = $derefSignal(useCounter()); count += 10; ``` compiles into ```js let [_count, _setcount] = useCounter(); _setcount(_current => _current += 10); ``` You can provide any kind of get/set tuples. ```js let value = $derefSignal([getValue, setValue]); value = newValue; effect: { console.log(value); } ``` ```js let [_value, _setvalue] = [getValue, setValue]; _setvalue(() => newValue); _createEffect(() => { console.log(_value()); }); ``` ### `$derefMemo` `$derefMemo` converts any kind of accessor into a memo variable. ```js const count = $derefMemo(useCounterValue()); effect: { console.log('Count:', count); } ``` compiles into ```js import { createEffect as _createEffect } from "solid-js"; const _count = useCounterValue(); _createEffect(() => { console.log('Count:', _count()); }); ``` ## Reactive properties ### `$getter` Assigns the given memo or signal ref to the property, transforming it into a getter property. Reading the property allows the assigned ref to be tracked. ```js let count = $signal(0); const obj = { count: $getter(count), }; ``` ```js import { createSignal as _createSignal } from "solid-js"; const _proto = { get count() { return this.__$get$count(); } }; let [_count, _setcount] = _createSignal(0); const obj = { __proto__: _proto, __$get$count: _count }; ``` ### `$setter` Assigns the given signal ref to the property, transforming it into a setter property. Setting the property allows the assigned ref to be reactively updated. ```js let count = $signal(0); const obj = { count: $setter(count), }; ``` ```js import { createSignal as _createSignal } from "solid-js"; const _proto = { set count(_param) { this.__$set$count(() => _param); } }; let [_count, _setcount] = _createSignal(0); const obj = { __proto__: _proto, __$set$count: _setcount }; ``` ### `$property` A combination of `$getter` and `$setter`. For memo refs, this only outputs the getter property. ```js let count = $signal(0); const message = $memo(`Count: ${count}`); const obj = { count: $property(count), message: $property(message), }; ``` ```js import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; const _proto = { get count() { return this.__$get$count(); }, set count(_param) { this.__$set$count(() => _param); }, get message() { return this.__$get$message(); } }; let [_count, _setcount] = _createSignal(0); const _message = _createMemo(() => `Count: ${_count()}`); const obj = { __proto__: _proto, __$get$count: _count, __$set$count: _setcount, __$get$message: _message }; ``` ## Store These CTFs are based from `solid-js/store` exports. - `$store` -> `createStore` - `$mutable` -> `createMutable` - `$reconcile` -> `reconcile` - `$produce` -> `produce` - `$unwrap` -> `unwrap` ## Components These CTFs are auto-imported components from `solid-js` and `solid-js/web`. You can still use their original identifiers since those are already supported by `babel-preset-solid`. ## Tooling On any `d.ts` file, add a reference markup ```ts /// ``` All CTFs are declared globally so there's no need to import. ================================================ FILE: docs/labels.md ================================================ # Labels ## Utilities ### `signal` Transforms into `createSignal`: ```js function Counter() { signal: x = 0; function increment() { x += 1; } return () => x; } ``` becomes ```js import { createSignal as _createSignal } from "solid-js"; function Counter() { const [_x, _setx] = _createSignal(0); function increment() { _setx(_current => _current += 1); } return () => _x(); } ``` Chained variable declaration is also supported. ```js signal: var x = 0, y = 0, z = 0; ``` ### `memo` Transforms into `createMemo`: ```js function Counter() { signal: x = 0; memo: message = `Count: ${x}`; return () => message; } ``` becomes ```js import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; function Counter() { const [_x, _setx] = _createSignal(0, { name: "x" }); const _message = _createMemo(() => `Count: ${_x()}`, undefined, { name: "message" }); return () => _message(); } ``` Chained variable declaration is also supported. ```js memo: var y = x + 10, z = y / 10; ``` ### `effect`, `computed` and `renderEffect` Transforms into `createEffect`, `createComputed` and `createRenderEffect`, respectively. ```js function Counter() { signal: x = 0; effect: { console.log('Count', x); } computed: { console.log('Count', x); } renderEffect: { console.log('Count', x); } } ``` into ```js import { createRenderEffect as _createRenderEffect } from "solid-js"; import { createComputed as _createComputed } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; function Counter() { const [_x, _setx] = _createSignal(0); _createEffect(() => { console.log('Count', _x()); }); _createComputed(() => { console.log('Count', _x()); }); _createRenderEffect(() => { console.log('Count', _x()); }); } ``` You may use an arrow function instead of a block statement to accept the previously returned value. If an expression (e.g. identifier, function call for `label: expr;`) is supplied, it compiles to `hook(expr)`. They can also be named by adding another labeled statement: ```js function Counter() { signal: x = 0; effect: effectLog: { console.log('Count', x); } computed: computedLog: { console.log('Count', x); } renderEffect: renderEffectLog: { console.log('Count', x); } } ``` ```js import { createRenderEffect as _createRenderEffect } from "solid-js"; import { createComputed as _createComputed } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; function Counter() { const [_x, _setx] = _createSignal(0); _createEffect(() => { console.log('Count', _x()); }, undefined, { name: "effectLog" }); _createComputed(() => { console.log('Count', _x()); }, undefined, { name: "computedLog" }); _createRenderEffect(() => { console.log('Count', _x()); }, undefined, { name: "renderEffectLog" }); } ``` ### `$` Similar to `memo` and `effect`, `$` compiles to `createMemo` for variable declaration, while `createEffect(() => expr)` for other kinds of expressions (including block statements). `$` is ideal for single-line effects. ```js let x = $signal(0); $: var y = x + 10; $: x = compute(); $: { console.log(x); } ``` compiles into ```js import { createEffect as _createEffect } from "solid-js"; import { createMemo as _createMemo } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [_x, _setx] = _createSignal(0, { name: "x" }); const _y = _createMemo(() => _x() + 10, undefined, { name: "y" }); _createEffect(() => _setx(() => compute())); _createEffect(() => { console.log(_x()); }); ``` ### `mount`, `cleanup` and `error` Transforms into `onMount`, `onCleanup` and `onError`. ```js function Counter() { mount: { console.log('Mounted!'); } cleanup: { console.log('Cleaned!'); } error: { console.log('Something went wrong.'); } } ``` into ```js import { onError as _onError } from "solid-js"; import { onCleanup as _onCleanup } from "solid-js"; import { onMount as _onMount } from "solid-js"; function Counter() { _onMount(() => { console.log('Mounted!'); }); _onCleanup(() => { console.log('Cleaned!'); }); _onError(() => { console.log('Something went wrong.'); }); } ``` You may also use an arrow function. For `onError`, an arrow function with a parameter may be used to receive the error object. If an expression (e.g. identifier, function call for `label: expr;`) is supplied, it compiles to `hook(expr)`. ### `untrack` and `batch` Transforms into `untrack` and `batch`. ```js function Counter() { batch: { console.log('This is batched!'); } untrack: { console.log('This is untracked!'); } } ``` into ```js import { untrack as _untrack } from "solid-js"; import { batch as _batch } from "solid-js"; function Counter() { _batch(() => { console.log('This is batched!'); }); _untrack(() => { console.log('This is untracked!'); }); } ``` You may also use an arrow function. If an expression (e.g. identifier, function call for `label: expr;`) is supplied, it compiles to `hook(expr)`. ### `root` Transforms into `createRoot` ```js root: { element = renderComponent(MyComponent); } ``` into ```js import { createRoot as _createRoot } from "solid-js"; _createRoot(() => { element = renderComponent(MyComponent); }); ``` You can also pass an arrow function instead of a block to receive the `dispose` callback. If an expression (e.g. identifier, function call for `label: expr;`) is supplied, it compiles to `hook(expr)`. ### `children` Compiles to `children`. ```js children: var nodes = props.children; ``` ```js import { children as _children } from "solid-js"; const _nodes = _children(() => props.children); ``` ### `deferred` Compiles to `createDeferred`. ```js signal: var searchInput = ''; deferred: var deferredSearchInput = searchInput; effect: { fetchResults(deferredSearchInput); } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { createDeferred as _createDeferred } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; const [_searchInput, _setsearchInput] = _createSignal('', { name: "searchInput" }); const _deferredSearchInput = _createDeferred(() => _searchInput(), { name: "deferredSearchInput" }); _createEffect(() => { fetchResults(_deferredSearchInput()); }); ``` ### `transition` Compiles to `startTransition`. Arrow function can be provided instead of blocks. ```js signal: var data; transition: { data = fetchData(); } ``` ```js import { startTransition as _startTransition } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; const [_data, _setdata] = _createSignal(undefined, { name: "data" }); _startTransition(() => { _setdata(() => fetchData()); }); ``` ### `destructure` Destructures an object while retaining reactivity. This partially compiles to `splitProps` if a rest expression is detected. `destructure` also supports nested destructures. Does not support default assignment. ```js destructure: var { a: { b, c }, b: { d, e }, ...f } = x; effect: { console.log(b, c); } effect: { console.log(d, e); } effect: { console.log(f); } ``` ```js import { createEffect as _createEffect } from "solid-js"; import { splitProps as _splitProps } from "solid-js"; const _prop = () => x.a, _prop2 = () => _prop().b, _prop3 = () => _prop().c, _other2 = _splitProps(_prop(), ["b", "c"])[1], _prop4 = () => x.b, _prop5 = () => _prop4().d, _prop6 = () => _prop4().e, _other3 = _splitProps(_prop4(), ["d", "e"])[1], _other = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log(_prop2(), _prop3()); }); _createEffect(() => { console.log(_prop5(), _prop6()); }); _createEffect(() => { console.log(_other); }); ``` ## Tooling ### TypeScript `tsconfig.json` ```json { "compilerOptions": { "allowUnusedLabels": true, } } ``` For `signal` and `memo` sugar, you'll need to use `@ts-ignore` to suppress warnings for TS1344 when using strict mode. ### ESLint ```json { "rules": { "no-var": "off", "no-restricted-syntax": "off", "no-labels": "off", "vars-on-top": "off", "no-unused-labels": "off" }, } ``` ================================================ FILE: docs/namespace.md ================================================ # Namespace ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Cfor%3E) ```jsx Loading...}> {(item) =>
{item}
}
``` ```jsx import { For as _For } from "solid-js"; <_For each={state.list} fallback={
Loading...
}> {item =>
{item}
} ; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Cindex%3E) ```jsx Loading...}> {(item) =>
{item()}
}
``` ```jsx import { Index as _Index } from "solid-js"; <_Index each={state.list} fallback={
Loading...
}> {item =>
{item()}
} ; ``` ## `` and `` Alias for [`` and ``](https://www.solidjs.com/docs/latest#%3Cswitch%3E%2F%3Cmatch%3E) ```jsx Not Found}> ``` ```jsx import { Match as _Match } from "solid-js"; import { Switch as _Switch } from "solid-js"; <_Switch fallback={
Not Found
}> <_Match when={state.route === "home"}> <_Match when={state.route === "settings"}> ; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Cshow%3E) ```jsx Loading...}> {(user) =>
{user.firstName}
}
``` ```jsx import { Show as _Show } from "solid-js"; <_Show when={state.user} fallback={
Loading...
}> {user =>
{user.firstName}
} ; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Cerrorboundary%3E) ```jsx
Error: {err.toString()}
} >
``` ```jsx import { ErrorBoundary as _ErrorBoundary } from "solid-js"; <_ErrorBoundary fallback={(err, reset) =>
Error: {err.toString()}
}> ; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Csuspense%3E) ```jsx Loading...}> ``` ```jsx import { Suspense as _Suspense } from "solid-js"; <_Suspense fallback={
Loading...
}> ; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Csuspenselist%3E-(experimental)) ```jsx Loading posts...}> Loading fun facts...}> ``` ```jsx import { Suspense as _Suspense } from "solid-js"; import { SuspenseList as _SuspenseList } from "solid-js"; <_SuspenseList revealOrder="forwards" tail="collapsed"> <_Suspense fallback={

Loading posts...

}> <_Suspense fallback={

Loading fun facts...

}> ; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Cdynamic%3E) ```jsx ``` ```jsx import { Dynamic as _Dynamic } from "solid-js/web"; <_Dynamic component={state.component} someProp={state.something} />; ``` ## `` Alias for [``](https://www.solidjs.com/docs/latest#%3Cportal%3E) ```jsx
My Content
``` ```jsx import { Portal as _Portal } from "solid-js/web"; <_Portal mount={document.getElementById("modal")}>
My Content
; ``` ## `` Alias for `` ```jsx ``` ```jsx import { Assets as _Assets } from "solid-js/web"; <_Assets> ; ``` ## `` Alias for `` ```jsx const App = () => { return ( 🔥 Solid SSR 🔥 {/*... rest of App*/} ); } ``` ```jsx import { HydrationScript as _HydrationScript } from "solid-js/web"; const App = () => { return 🔥 Solid SSR 🔥 <_HydrationScript /> { /*... rest of App*/ } ; }; ``` ## `` Alias for `` ```jsx ``` ```jsx import { NoHydration as _NoHydration } from "solid-js/web"; <_NoHydration> ; ``` ================================================ FILE: examples/comments/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local ================================================ FILE: examples/comments/index.html ================================================ Vite App
================================================ FILE: examples/comments/package.json ================================================ { "name": "vite-example-comments", "type": "module", "version": "0.17.0", "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview" }, "devDependencies": { "solid-labels": "0.17.0", "typescript": "^5.8.2", "vite": "^6.1.1", "vite-plugin-solid": "^2.11.2", "vite-plugin-solid-labels": "0.17.0" }, "private": true, "publishConfig": { "access": "restricted" }, "dependencies": { "solid-js": "^1.9.3" } } ================================================ FILE: examples/comments/src/App.tsx ================================================ import type { JSX } from 'solid-js/jsx-runtime'; export default function App(): JSX.Element { /* @signal */ let count = 0; /* @memo */ const message = `Count: ${count}`; /* @effect */ { console.log(message); } function increment(): void { count += 1; } return ( ); } ================================================ FILE: examples/comments/src/main.tsx ================================================ import { render } from 'solid-js/web'; import App from './App'; const app = document.getElementById('app'); if (app) { render(() => , app); } ================================================ FILE: examples/comments/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "moduleResolution": "Node", "strict": true, "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "allowUnusedLabels": true, "noImplicitReturns": true, "jsx": "preserve", "jsxImportSource": "solid-js", "types": [ "vite/client", "solid-labels" ] }, "include": ["./src"] } ================================================ FILE: examples/comments/vite.config.js ================================================ import { defineConfig } from 'vite'; import solidPlugin from 'vite-plugin-solid'; import solidLabels from 'vite-plugin-solid-labels'; export default defineConfig({ plugins: [ solidPlugin({}), solidLabels({ filter: { include: 'src/**/*.{ts,js,tsx,jsx}', exclude: 'node_modules/**/*.{ts,js,tsx,jsx}', }, }), ], }); ================================================ FILE: examples/ctf/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local ================================================ FILE: examples/ctf/index.html ================================================ Vite App
================================================ FILE: examples/ctf/package.json ================================================ { "name": "vite-example-ctf", "type": "module", "version": "0.17.0", "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview" }, "devDependencies": { "solid-labels": "0.17.0", "typescript": "^5.8.2", "vite": "^6.1.1", "vite-plugin-solid": "^2.11.2", "vite-plugin-solid-labels": "0.17.0" }, "private": true, "publishConfig": { "access": "restricted" }, "dependencies": { "solid-js": "^1.9.3" } } ================================================ FILE: examples/ctf/src/App.tsx ================================================ import type { Accessor, JSX, Setter } from 'solid-js'; function useCounter(): [Accessor, Setter] { const count = $signal(0); $effect(() => { console.log('Current count:', count); }); return $refSignal(count); } export default function App(): JSX.Element { let count = $derefSignal(useCounter()); const message = $memo(`Count: ${count}`); $effect(() => { console.log(message); }); function increment(): void { count += 1; } return ( <> Odd}>

Even

); } ================================================ FILE: examples/ctf/src/main.tsx ================================================ import { render } from 'solid-js/web'; import App from './App'; const app = document.getElementById('app'); if (app) { render(() => , app); } ================================================ FILE: examples/ctf/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "moduleResolution": "Node", "strict": true, "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "allowUnusedLabels": true, "noImplicitReturns": true, "jsx": "preserve", "jsxImportSource": "solid-js", "types": [ "vite/client", "solid-labels" ] }, "include": ["./src"] } ================================================ FILE: examples/ctf/vite.config.js ================================================ import { defineConfig } from 'vite'; import solidPlugin from 'vite-plugin-solid'; import solidLabels from 'vite-plugin-solid-labels'; export default defineConfig({ plugins: [ solidPlugin({}), solidLabels({ filter: { include: 'src/**/*.{ts,js,tsx,jsx}', exclude: 'node_modules/**/*.{ts,js,tsx,jsx}', }, }), ], }); ================================================ FILE: examples/labels/.gitignore ================================================ node_modules .DS_Store dist dist-ssr *.local ================================================ FILE: examples/labels/index.html ================================================ Vite App
================================================ FILE: examples/labels/package.json ================================================ { "name": "vite-example-labels", "type": "module", "version": "0.17.0", "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview" }, "devDependencies": { "solid-labels": "0.17.0", "typescript": "^5.8.2", "vite": "^6.1.1", "vite-plugin-solid": "^2.11.2", "vite-plugin-solid-labels": "0.17.0" }, "private": true, "publishConfig": { "access": "restricted" }, "dependencies": { "solid-js": "^1.9.3" } } ================================================ FILE: examples/labels/src/App.tsx ================================================ import type { JSX } from 'solid-js/jsx-runtime'; export default function App(): JSX.Element { signal: var count = 0; memo: var message = `Count: ${count}`; effect: { console.log(message); } function increment(): void { count += 1; } return ( ); } ================================================ FILE: examples/labels/src/main.tsx ================================================ import { render } from 'solid-js/web'; import App from './App'; const app = document.getElementById('app'); if (app) { render(() => , app); } ================================================ FILE: examples/labels/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "moduleResolution": "Node", "strict": true, "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "allowUnusedLabels": true, "noImplicitReturns": true, "jsx": "preserve", "jsxImportSource": "solid-js", "types": [ "vite/client", "solid-labels" ] }, "include": ["./src"] } ================================================ FILE: examples/labels/vite.config.js ================================================ import { defineConfig } from 'vite'; import solidPlugin from 'vite-plugin-solid'; import solidLabels from 'vite-plugin-solid-labels'; export default defineConfig({ plugins: [ solidPlugin({}), solidLabels({ filter: { include: 'src/**/*.{ts,js,tsx,jsx}', exclude: 'node_modules/**/*.{ts,js,tsx,jsx}', }, }), ], }); ================================================ FILE: lerna.json ================================================ { "npmClient": "npm", "packages": [ "packages/*", "examples/*" ], "command": { "version": { "exact": true }, "publish": { "allowBranch": [ "main" ], "registry": "https://registry.npmjs.org/" } }, "version": "0.17.0" } ================================================ FILE: package.json ================================================ { "name": "root", "private": true, "workspaces": ["packages/*", "examples/*"], "devDependencies": { "@biomejs/biome": "^1.9.4", "lerna": "^8.2.1", "typescript": "^5.8.2" } } ================================================ FILE: packages/core/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.production .env.development # parcel-bundler cache (https://parceljs.org/) .cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and *not* Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port .npmrc ================================================ FILE: packages/core/README.md ================================================ # `solid-labels` [![NPM](https://img.shields.io/npm/v/solid-labels.svg)](https://www.npmjs.com/package/solid-labels) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript)

Example

## Install ```bash npm install solid-labels ``` ```bash yarn add solid-labels ``` ```bash pnpm add solid-labels ``` ## Features - 🏷 Labels: Turn labels into SolidJS utility calls! - 💬 Comments: Turn comments into SolidJS utility calls, too! - ⏱ Compile-time Functions: Use functions that are evaluated during compile-time! - 📦 Auto Imports: No need to import SolidJS utilities, explicitly! - 🤝 JS and TS Friendly! ## Usage - [Labels](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/labels.md) [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/solid-labels/tree/main/examples/labels) - [Comments](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/comments.md) [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/solid-labels/tree/main/examples/comments) - [Compile-Time Functions](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/ctf.md) [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/solid-labels/tree/main/examples/ctf) - [Solid Namespace](https://github.com/LXSMNSYC/solid-labels/tree/main/docs/namespace.md) ### Typescript `.d.ts` ```ts /// ``` ### Babel `.babelrc` ```json { "plugins": [ ["solid-labels/babel", { "dev": false }] ] } ``` [!INFO]: You don't have to use this if you're using Vite or Rollup plugins ## Integrations - [Vite](https://github.com/lxsmnsyc/solid-labels/tree/main/packages/vite) - [Rollup](https://github.com/lxsmnsyc/solid-labels/tree/main/packages/rollup) - [Unplugin](https://github.com/lxsmnsyc/solid-labels/tree/main/packages/unplugin) ### Disabling features You can disable some features by passing `disabled` option to the plugin options. ```js { disabled: { labels: { signal: true, }, pragma: { '@signal': true, }, ctf: { $signal: true, }, } } ``` ## Limitations - Detecting shadowed identifier for `signal` and `memo`. ## Sponsors ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) ## License MIT © [lxsmnsyc](https://github.com/lxsmnsyc) ================================================ FILE: packages/core/babel/components.ts ================================================ import type { NodePath, Visitor } from '@babel/traverse'; import * as t from '@babel/types'; import { getImportIdentifier } from './core/get-import-identifier'; import type { State } from './core/types'; type ComponentImport = [name: string, source: string]; const COMPONENTS: Record = { // Components For: ['For', 'solid-js'], Show: ['Show', 'solid-js'], Switch: ['Switch', 'solid-js'], Match: ['Match', 'solid-js'], Index: ['Index', 'solid-js'], ErrorBoundary: ['ErrorBoundary', 'solid-js'], Suspense: ['Suspense', 'solid-js'], SuspenseList: ['SuspenseList', 'solid-js'], Dynamic: ['Dynamic', 'solid-js/web'], Portal: ['Portal', 'solid-js/web'], Assets: ['Assets', 'solid-js/web'], HydrationScript: ['HydrationScript', 'solid-js/web'], NoHydration: ['NoHydration', 'solid-js/web'], }; const NAMESPACE = 'solid'; const NAMESPACE_COMPONENTS: Record = { // Components for: ['For', 'solid-js'], show: ['Show', 'solid-js'], switch: ['Switch', 'solid-js'], match: ['Match', 'solid-js'], index: ['Index', 'solid-js'], 'error-boundary': ['ErrorBoundary', 'solid-js'], suspense: ['Suspense', 'solid-js'], 'suspense-list': ['SuspenseList', 'solid-js'], dynamic: ['Dynamic', 'solid-js/web'], portal: ['Portal', 'solid-js/web'], assets: ['Assets', 'solid-js/web'], 'hydration-script': ['HydrationScript', 'solid-js/web'], 'no-hydration': ['NoHydration', 'solid-js/web'], }; const COMPONENT_TRAVERSE: Visitor = { Expression(p, state) { if ( t.isIdentifier(p.node) && !p.scope.hasBinding(p.node.name) && p.node.name in COMPONENTS ) { const [name, source] = COMPONENTS[p.node.name]; p.replaceWith(getImportIdentifier(state, p, name, source)); } }, JSXElement(p, state) { const { openingElement, closingElement } = p.node; const id = openingElement.name; let replacement: t.JSXIdentifier | undefined; if ( t.isJSXNamespacedName(id) && id.namespace.name === NAMESPACE && id.name.name in NAMESPACE_COMPONENTS ) { const [name, source] = NAMESPACE_COMPONENTS[id.name.name]; const identifier = getImportIdentifier(state, p, name, source); replacement = t.jsxIdentifier(identifier.name); } if ( t.isJSXIdentifier(id) && !p.scope.hasBinding(id.name) && id.name in COMPONENTS ) { const [name, source] = COMPONENTS[id.name]; const identifier = getImportIdentifier(state, p, name, source); replacement = t.jsxIdentifier(identifier.name); } if (replacement) { openingElement.name = replacement; if (closingElement) { closingElement.name = replacement; } } }, }; export function transformComponents(state: State, path: NodePath): void { path.traverse(COMPONENT_TRAVERSE, state); } ================================================ FILE: packages/core/babel/constants.ts ================================================ import * as t from '@babel/types'; export const UNDEFINED = t.unaryExpression('void', t.numericLiteral(0)); ================================================ FILE: packages/core/babel/core/accessor-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { derefMemo } from './deref-memo'; import { generateUniqueName } from './generate-unique-name'; export function accessorVariable( path: babel.NodePath, accessorIdentifier: t.Identifier, callee: t.Identifier, replacement: Array, ): t.VariableDeclarator { const readIdentifier = generateUniqueName(path, accessorIdentifier.name); derefMemo(path, accessorIdentifier, readIdentifier); return t.variableDeclarator( readIdentifier, t.callExpression(callee, replacement), ); } ================================================ FILE: packages/core/babel/core/assert.ts ================================================ export function assert(cond: unknown, error: T): asserts cond { if (!cond) { throw error; } } ================================================ FILE: packages/core/babel/core/deferred-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { accessorVariable } from './accessor-variable'; import { getImportIdentifier } from './get-import-identifier'; import type { State } from './types'; import { UNDEFINED } from '../constants'; export function deferredVariable( state: State, path: babel.NodePath, deferredIdentifier: t.Identifier, stateIdentifier: t.Expression = UNDEFINED, optionsIdentifier: t.Expression | undefined = undefined, ): t.VariableDeclarator { const normalIdentifier = t.arrowFunctionExpression([], stateIdentifier); const args: t.Expression[] = [normalIdentifier]; if (state.opts.dev) { if (optionsIdentifier) { args.push( t.callExpression( t.memberExpression(t.identifier('Object'), t.identifier('assign')), [ t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(deferredIdentifier.name), ), ]), optionsIdentifier, ], ), ); } else { args.push( t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(deferredIdentifier.name), ), ]), ); } } else if (optionsIdentifier) { args.push(optionsIdentifier); } return accessorVariable( path, deferredIdentifier, getImportIdentifier(state, path, 'createDeferred', 'solid-js'), args, ); } ================================================ FILE: packages/core/babel/core/deref-memo-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { derefMemo } from './deref-memo'; import { generateUniqueName } from './generate-unique-name'; export function derefMemoVariable( path: babel.NodePath, memoIdentifier: t.Identifier, stateIdentifier: t.Expression, ): t.VariableDeclarator { const readIdentifier = generateUniqueName(path, memoIdentifier.name); derefMemo(path, memoIdentifier, readIdentifier); return t.variableDeclarator(readIdentifier, stateIdentifier); } ================================================ FILE: packages/core/babel/core/deref-memo.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { assert } from './assert'; import { unexpectedType } from './errors'; import { addProtoGetter } from './proto'; import { getProperParentPath, unwrapNode } from './unwrap-node'; const REF_MEMO_CTF = '$refMemo'; const GET_CTF = '$get'; const GETTER_CTF = '$getter'; const PROPERTY_CTF = '$property'; const CALL_CTF = new Set([REF_MEMO_CTF, GET_CTF, GETTER_CTF, PROPERTY_CTF]); function transformGetter( parent: babel.NodePath, readIdentifier: t.Identifier, ): boolean { const propertyParent = getProperParentPath(parent, t.isObjectProperty); if (!propertyParent) { return true; } const key = propertyParent.node.key; assert( t.isExpression(key), unexpectedType(propertyParent.get('key'), key.type, 'Identifier'), ); const objectParent = getProperParentPath( propertyParent, t.isObjectExpression, ); if (!objectParent) { return true; } addProtoGetter(objectParent, propertyParent, key, readIdentifier); return false; } function transformReferencePath( ref: babel.NodePath, readIdentifier: t.Identifier, ): boolean { const parent = getProperParentPath(ref, t.isCallExpression); if (parent) { const trueCallee = unwrapNode(parent.node.callee, t.isIdentifier); if (!(trueCallee && CALL_CTF.has(trueCallee.name))) { return true; } const rawArgs = parent.get('arguments')[0]; const arg = unwrapNode(rawArgs.node, t.isIdentifier); assert(arg, unexpectedType(rawArgs, rawArgs.type, 'Identifier')); if (arg !== ref.node) { return true; } switch (trueCallee.name) { case REF_MEMO_CTF: case GET_CTF: parent.replaceWith(readIdentifier); break; case PROPERTY_CTF: case GETTER_CTF: return transformGetter(parent, readIdentifier); } return false; } return true; } export function derefMemo( path: babel.NodePath, memoIdentifier: t.Identifier, readIdentifier: t.Identifier, ): void { const binding = path.scope.getBinding(memoIdentifier.name); if (!binding) { return; } for (const ref of binding.referencePaths) { if (transformReferencePath(ref, readIdentifier)) { assert( t.isIdentifier(ref.node), unexpectedType(ref, ref.node.type, 'Identifier'), ); ref.replaceWith(t.callExpression(readIdentifier, [])); } } } ================================================ FILE: packages/core/babel/core/deref-signal-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { derefSignal } from './deref-signal'; import { generateUniqueName } from './generate-unique-name'; export function derefSignalVariable( path: babel.NodePath, signalIdentifier: t.Identifier, stateIdentifier: t.Expression, ): t.VariableDeclarator { const readIdentifier = generateUniqueName(path, signalIdentifier.name); const writeIdentifier = generateUniqueName( path, `set${signalIdentifier.name}`, ); derefSignal(path, signalIdentifier, readIdentifier, writeIdentifier); return t.variableDeclarator( t.arrayPattern([readIdentifier, writeIdentifier]), stateIdentifier, ); } ================================================ FILE: packages/core/babel/core/deref-signal.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { assert } from './assert'; import { unexpectedType } from './errors'; import { generateUniqueName } from './generate-unique-name'; import { isAwaited } from './is-awaited'; import { isYielded } from './is-yielded'; import { addProtoGetter, addProtoProperty, addProtoSetter } from './proto'; import { getProperParentPath, isPathValid, unwrapNode } from './unwrap-node'; const REF_SIGNAL_CTF = '$refSignal'; const GET_CTF = '$get'; const SET_CTF = '$set'; const GETTER_CTF = '$getter'; const SETTER_CTF = '$setter'; const PROPERTY_CTF = '$property'; const CALL_CTF = new Set([ REF_SIGNAL_CTF, GET_CTF, SET_CTF, GETTER_CTF, SETTER_CTF, PROPERTY_CTF, ]); function transformProperty( parent: babel.NodePath, ctf: string, readIdentifier: t.Identifier, writeIdentifier: t.Identifier, ): boolean { const propertyParent = getProperParentPath(parent, t.isObjectProperty); if (!propertyParent) { return true; } const key = propertyParent.node.key; assert( t.isExpression(key), unexpectedType(propertyParent.get('key'), key.type, 'Identifier'), ); const objectParent = getProperParentPath( propertyParent, t.isObjectExpression, ); if (!objectParent) { return true; } switch (ctf) { case SETTER_CTF: addProtoSetter(objectParent, propertyParent, key, writeIdentifier); break; case GETTER_CTF: addProtoGetter(objectParent, propertyParent, key, writeIdentifier); break; case PROPERTY_CTF: addProtoProperty( objectParent, propertyParent, key, readIdentifier, writeIdentifier, ); break; } return false; } function transformSignalRead( ref: babel.NodePath, readIdentifier: t.Identifier, writeIdentifier: t.Identifier, ): boolean { const parent = getProperParentPath(ref, t.isCallExpression); if (parent) { const trueCallee = unwrapNode(parent.node.callee, t.isIdentifier); if (!(trueCallee && CALL_CTF.has(trueCallee.name))) { return true; } const rawArgs = parent.get('arguments')[0]; const arg = unwrapNode(rawArgs.node, t.isIdentifier); assert(arg, unexpectedType(rawArgs, rawArgs.type, 'Identifier')); if (arg !== ref.node) { return true; } switch (trueCallee.name) { case REF_SIGNAL_CTF: parent.replaceWith( t.arrayExpression([readIdentifier, writeIdentifier]), ); break; case SET_CTF: parent.replaceWith(writeIdentifier); break; case GET_CTF: parent.replaceWith(readIdentifier); break; case PROPERTY_CTF: case SETTER_CTF: case GETTER_CTF: return transformProperty( parent, trueCallee.name, readIdentifier, writeIdentifier, ); } return false; } return true; } function transformUpdateExpression( ref: babel.NodePath, writeIdentifier: t.Identifier, ): void { const param = generateUniqueName(ref, 'current'); if (ref.node.prefix) { const tmp = generateUniqueName(ref, 'tmp'); ref.replaceWith( t.callExpression( t.arrowFunctionExpression( [], t.blockStatement([ t.variableDeclaration('let', [t.variableDeclarator(tmp)]), t.expressionStatement( t.callExpression(writeIdentifier, [ t.arrowFunctionExpression( [param], t.binaryExpression( ref.node.operator === '++' ? '+' : '-', t.assignmentExpression('=', tmp, param), t.numericLiteral(1), ), ), ]), ), t.returnStatement(tmp), ]), ), [], ), ); } else { ref.replaceWith( t.callExpression(writeIdentifier, [ t.arrowFunctionExpression( [param], t.binaryExpression( ref.node.operator === '++' ? '+' : '-', param, t.numericLiteral(1), ), ), ]), ); } } function transformAssignmentExpression( ref: babel.NodePath, writeIdentifier: t.Identifier, ): void { assert( t.isIdentifier(ref.node.left), unexpectedType(ref.get('left'), ref.node.left.type, 'Identifier'), ); let expression = ref.node.right; if (isAwaited(expression) || isYielded(expression)) { const statement = ref.getStatementParent(); const functionParent = ref.getFunctionParent(); if (statement) { const awaitedID = generateUniqueName(statement, 'tmp'); const declaration = t.variableDeclaration('const', [ t.variableDeclarator(awaitedID, expression), ]); if (functionParent) { if (functionParent.isAncestor(statement)) { statement.insertBefore(declaration); } else { functionParent.scope.push({ id: awaitedID, init: expression, kind: 'const', }); } } else { statement.insertBefore(declaration); } expression = awaitedID; } } let arg: t.Expression; if (ref.node.operator === '=') { arg = t.arrowFunctionExpression([], expression); } else { const param = generateUniqueName(ref, 'current'); arg = t.arrowFunctionExpression( [param], t.assignmentExpression(ref.node.operator, param, expression), ); } ref.replaceWith(t.callExpression(writeIdentifier, [arg])); } function transformSignalWrite( ref: babel.NodePath, writeIdentifier: t.Identifier, ): void { if (isPathValid(ref, t.isUpdateExpression)) { transformUpdateExpression(ref, writeIdentifier); return; } if (isPathValid(ref, t.isAssignmentExpression)) { transformAssignmentExpression(ref, writeIdentifier); return; } } export function derefSignal( path: babel.NodePath, signalIdentifier: t.Identifier, readIdentifier: t.Identifier, writeIdentifier: t.Identifier, ): void { const binding = path.scope.getBinding(signalIdentifier.name); if (!binding) { return; } // Transform all writes for (const ref of binding.constantViolations) { transformSignalWrite(ref, writeIdentifier); } // Transform all reads for (const ref of binding.referencePaths) { if (transformSignalRead(ref, readIdentifier, writeIdentifier)) { assert( t.isIdentifier(ref.node), unexpectedType(ref, ref.node.type, 'Identifier'), ); ref.replaceWith(t.callExpression(readIdentifier, [])); } } } ================================================ FILE: packages/core/babel/core/destructure-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { derefMemo } from './deref-memo'; import { unexpectedType } from './errors'; import { generateUniqueName } from './generate-unique-name'; import { getImportIdentifier } from './get-import-identifier'; import { isStatic } from './is-static'; import type { State } from './types'; import { unwrapNode } from './unwrap-node'; import { UNDEFINED } from '../constants'; export function destructureVariable( state: State, path: babel.NodePath, target: t.Expression, pattern: t.ObjectPattern | t.ArrayPattern, defaultValue?: t.Expression, ): t.VariableDeclarator[] { const otherIdentifier = generateUniqueName(path, 'other'); let declarators: t.VariableDeclarator[] = []; const properties: t.Expression[] = []; let restIdentifier: t.Identifier | undefined; // Destructuring for object patterns if (t.isObjectPattern(pattern)) { for (let i = 0, len = pattern.properties.length; i < len; i++) { const property = pattern.properties[i]; // Check if this is an object property if (t.isObjectProperty(property)) { const { value, key } = property; if (!property.computed) { if (t.isIdentifier(key)) { properties.push(t.stringLiteral(key.name)); } } else if (t.isPrivateName(key)) { throw unexpectedType(path, 'PrivateName', 'Expression'); } else { properties.push(key); } // Create a new identifier for the destructure variable const newIdentifier = generateUniqueName(path, 'prop'); let access: t.Expression | t.BlockStatement = t.memberExpression( target, key, property.computed, ); let defaultIdentifier: t.Identifier | undefined; if (t.isAssignmentPattern(value)) { defaultIdentifier = generateUniqueName(path, 'def'); const isStaticValue = isStatic(value.right); const defValue = isStaticValue ? value.right : t.callExpression( getImportIdentifier(state, path, 'createMemo', 'solid-js'), [t.arrowFunctionExpression([], value.right)], ); declarators.push(t.variableDeclarator(defaultIdentifier, defValue)); const valueIdentifier = generateUniqueName(path, 'value'); access = t.blockStatement([ t.variableDeclaration('const', [ t.variableDeclarator(valueIdentifier, access), ]), t.returnStatement( t.conditionalExpression( t.binaryExpression('===', valueIdentifier, UNDEFINED), isStaticValue ? defaultIdentifier : t.callExpression(defaultIdentifier, []), valueIdentifier, ), ), ]); } declarators.push( t.variableDeclarator( newIdentifier, t.arrowFunctionExpression([], access), ), ); // If the value is an object or array pattern // destructure that value again // e.g. { x: { y, z }} = w; if (t.isObjectPattern(value) || t.isArrayPattern(value)) { declarators = [ ...declarators, ...destructureVariable( state, path, t.callExpression(newIdentifier, []), value, ), ]; } else if (t.isIdentifier(value)) { // If the value is just a normal identifier // e.g. { x: y } = w; // normalize bindings derefMemo(path, value, newIdentifier); } else if (t.isAssignmentPattern(value)) { if (t.isIdentifier(value.left)) { // If the value has a default value derefMemo(path, value.left, newIdentifier); } else if ( t.isArrayPattern(value.left) || t.isObjectPattern(value.left) ) { // Otherwise it's just another array/object declarators = [ ...declarators, ...destructureVariable( state, path, t.callExpression(newIdentifier, []), value.left, defaultIdentifier, ), ]; } } else { throw unexpectedType( path, value.type, 'Identifier | ObjectPattern | ArrayPattern', ); } } else { // or it's a rest element // make sure that it is an identifier though const trueIdentifier = unwrapNode(property.argument, t.isIdentifier); if (trueIdentifier) { restIdentifier = trueIdentifier; } } } } else { // Destructure for arrays for (let i = 0, len = pattern.elements.length; i < len; i++) { const property = pattern.elements[i]; if (property) { const keyExpr = t.numericLiteral(i); const newIdentifier = generateUniqueName(path, 'prop'); let access: t.Expression | t.BlockStatement = t.memberExpression( target, keyExpr, true, ); let defaultIdentifier: t.Identifier | undefined; if (t.isAssignmentPattern(property)) { defaultIdentifier = generateUniqueName(path, 'def'); const isStaticValue = isStatic(property.right); const defValue = isStaticValue ? property.right : t.callExpression( getImportIdentifier(state, path, 'createMemo', 'solid-js'), [t.arrowFunctionExpression([], property.right)], ); declarators.push(t.variableDeclarator(defaultIdentifier, defValue)); const valueIdentifier = generateUniqueName(path, 'value'); access = t.blockStatement([ t.variableDeclaration('const', [ t.variableDeclarator(valueIdentifier, access), ]), t.returnStatement( t.conditionalExpression( t.binaryExpression('===', valueIdentifier, UNDEFINED), isStaticValue ? defaultIdentifier : t.callExpression(defaultIdentifier, []), valueIdentifier, ), ), ]); } declarators.push( t.variableDeclarator( newIdentifier, t.arrowFunctionExpression([], access), ), ); properties.push(keyExpr); if (t.isIdentifier(property)) { derefMemo(path, property, newIdentifier); } else if (t.isAssignmentPattern(property)) { if (t.isIdentifier(property.left)) { derefMemo(path, property.left, newIdentifier); } else if ( t.isArrayPattern(property.left) || t.isObjectPattern(property.left) ) { // Otherwise it's just another array/object declarators = [ ...declarators, ...destructureVariable( state, path, t.callExpression(newIdentifier, []), property.left, defaultIdentifier, ), ]; } } else if (t.isArrayPattern(property) || t.isObjectPattern(property)) { // Otherwise it's just another array/object declarators = [ ...declarators, ...destructureVariable( state, path, t.callExpression(newIdentifier, []), property, ), ]; } else if (t.isRestElement(property)) { const trueIdentifier = unwrapNode(property.argument, t.isIdentifier); if (trueIdentifier) { restIdentifier = trueIdentifier; } } } } } const expr = t.variableDeclarator( otherIdentifier, properties.length ? t.memberExpression( t.callExpression( getImportIdentifier(state, path, 'splitProps', 'solid-js'), [ defaultValue != null ? t.callExpression( getImportIdentifier(state, path, 'mergeProps', 'solid-js'), [target, defaultValue], ) : target, t.arrayExpression(properties), ], ), t.numericLiteral(1), true, ) : target, ); declarators.push(expr); if (restIdentifier) { const binding = path.scope.getBinding(restIdentifier.name); if (binding) { for (const ref of binding.referencePaths) { ref.replaceWith(otherIdentifier); } } } return declarators; } ================================================ FILE: packages/core/babel/core/errors.ts ================================================ import type * as babel from '@babel/core'; export function unexpectedType( path: babel.NodePath, received: string, expected: string, ): Error { return path.buildCodeFrameError( `Unexpected '${received}' (Expected: ${expected})`, ); } export function unexpectedMissingParent(path: babel.NodePath): Error { return path.buildCodeFrameError('Unexpected missing parent.'); } export function unexpectedArgumentLength( path: babel.NodePath, received: number, expected: number, ): Error { return path.buildCodeFrameError( `Unexpected argument length of ${received} (Expected: ${expected})`, ); } export function unexpectedAssignmentOperator( path: babel.NodePath, received: string, expected: string, ): Error { return path.buildCodeFrameError( `Unexpected assignment operator '${received}' (Expected: ${expected})`, ); } ================================================ FILE: packages/core/babel/core/generate-unique-name.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; export function generateUniqueName( path: babel.NodePath, name: string, ): t.Identifier { let uid; let i = 1; do { uid = name + '_' + i; i++; } while ( path.scope.hasLabel(uid) || path.scope.hasBinding(uid) || path.scope.hasGlobal(uid) || path.scope.hasReference(uid) ); const program = path.scope.getProgramParent(); program.references[uid] = true; program.uids[uid] = true; return t.identifier(uid); } ================================================ FILE: packages/core/babel/core/get-import-identifier.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import type { State } from './types'; export function getImportIdentifier( state: State, path: babel.NodePath, name: string, source: string, ): t.Identifier { const target = `${source}[${name}]`; const current = state.hooks.get(target); if (current) { return current; } const programParent = path.scope.getProgramParent(); const uid = programParent.generateUidIdentifier(name); const newPath = ( programParent.path as babel.NodePath ).unshiftContainer( 'body', t.importDeclaration( [t.importSpecifier(uid, t.identifier(name))], t.stringLiteral(source), ), )[0]; programParent.registerDeclaration(newPath); state.hooks.set(target, uid); return uid; } ================================================ FILE: packages/core/babel/core/is-awaited.ts ================================================ import * as t from '@babel/types'; export function isAwaited(node: t.Expression | t.SpreadElement): boolean { // Default if (t.isAwaitExpression(node)) { return true; } if (t.isTemplateLiteral(node)) { return node.expressions.some( expr => t.isExpression(expr) && isAwaited(expr), ); } if ( t.isLiteral(node) || t.isIdentifier(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node) || t.isClassExpression(node) || t.isYieldExpression(node) || t.isJSX(node) || t.isMetaProperty(node) || t.isSuper(node) || t.isThisExpression(node) || t.isImport(node) || t.isDoExpression(node) ) { return false; } if (t.isTaggedTemplateExpression(node)) { return isAwaited(node.tag) || isAwaited(node.quasi); } if ( t.isUnaryExpression(node) || t.isUpdateExpression(node) || t.isSpreadElement(node) ) { return isAwaited(node.argument); } if ( t.isParenthesizedExpression(node) || t.isTypeCastExpression(node) || t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node) || t.isTSNonNullExpression(node) || t.isTSTypeAssertion(node) || t.isTSInstantiationExpression(node) ) { return isAwaited(node.expression); } // Check for elements if (t.isArrayExpression(node) || t.isTupleExpression(node)) { return node.elements.some(el => el != null && isAwaited(el)); } // Skip arrow function if (t.isAssignmentExpression(node)) { if (isAwaited(node.right)) { return true; } if (t.isExpression(node.left)) { return isAwaited(node.left); } return false; } if (t.isBinaryExpression(node)) { if (t.isExpression(node.left) && isAwaited(node.left)) { return true; } return isAwaited(node.right); } if ( t.isCallExpression(node) || t.isOptionalCallExpression(node) || t.isNewExpression(node) ) { if (t.isExpression(node.callee) && isAwaited(node.callee)) { return true; } return node.arguments.some( arg => arg && (t.isSpreadElement(arg) || t.isExpression(arg)) && isAwaited(arg), ); } if (t.isConditionalExpression(node)) { return ( isAwaited(node.test) || isAwaited(node.consequent) || isAwaited(node.alternate) ); } if (t.isLogicalExpression(node)) { return isAwaited(node.left) || isAwaited(node.right); } if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) { return ( isAwaited(node.object) || (node.computed && t.isExpression(node.property) && isAwaited(node.property)) ); } if (t.isSequenceExpression(node)) { return node.expressions.some(isAwaited); } if (t.isObjectExpression(node) || t.isRecordExpression(node)) { return node.properties.some(prop => { if (t.isObjectProperty(prop)) { if (t.isExpression(prop.value) && isAwaited(prop.value)) { return true; } if (prop.computed && t.isExpression(prop.key) && isAwaited(prop.key)) { return true; } return false; } if (t.isSpreadElement(prop)) { return isAwaited(prop); } return false; }); } if (t.isBindExpression(node)) { return isAwaited(node.object) || isAwaited(node.callee); } return false; } ================================================ FILE: packages/core/babel/core/is-in-typescript.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; export function isInTypescript(path: babel.NodePath): boolean { let parent = path.parentPath; while (parent) { if (t.isTypeScript(parent.node) && !t.isExpression(parent.node)) { return true; } parent = parent.parentPath; } return false; } ================================================ FILE: packages/core/babel/core/is-static.ts ================================================ import * as t from '@babel/types'; export function isStatic( node: | t.Expression | t.SpreadElement | t.AssignmentPattern | t.ArrayPattern | t.ObjectPattern | t.RestElement, ): boolean { // The following types are singular nested expressions if ( t.isParenthesizedExpression(node) || t.isTypeCastExpression(node) || t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node) || t.isTSNonNullExpression(node) || t.isTSTypeAssertion(node) || t.isTSInstantiationExpression(node) ) { return isStatic(node.expression); } // Same as above if ( t.isUnaryExpression(node) || t.isUpdateExpression(node) || t.isSpreadElement(node) ) { return isStatic(node.argument); } if (t.isRestElement(node)) { if (t.isTSParameterProperty(node.argument)) { return false; } return isStatic(node.argument); } if (t.isLiteral(node)) { if (t.isTemplateLiteral(node)) { return node.expressions.every(expr => { if (t.isExpression(expr)) { return isStatic(expr); } return false; }); } return true; } // The following types are always static if ( t.isIdentifier(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node) // || // t.isJSXElement(node) || // t.isJSXFragment(node) ) { return true; } // Arrays and tuples might have static values if (t.isArrayExpression(node) || t.isTupleExpression(node)) { return node.elements.every(el => { if (el) { return isStatic(el); } return true; }); } if (t.isArrayPattern(node)) { return node.elements.every(el => { if (t.isTSParameterProperty(el)) { return false; } if (el) { return isStatic(el); } return true; }); } if (t.isObjectExpression(node) || t.isRecordExpression(node)) { return node.properties.every(prop => { if (t.isObjectProperty(prop)) { if (t.isExpression(prop.value) && isStatic(prop.value)) { return true; } if (prop.computed && t.isExpression(prop.key) && isStatic(prop.key)) { return true; } return false; } if (t.isSpreadElement(prop)) { return isStatic(prop); } // Ignore return true; }); } if (t.isObjectPattern(node)) { return node.properties.every(prop => { if (t.isObjectProperty(prop)) { if (!t.isTSTypeParameter(prop.value) && isStatic(prop.value)) { return true; } if (prop.computed && t.isExpression(prop.key) && isStatic(prop.key)) { return true; } return false; } if (t.isSpreadElement(prop)) { return isStatic(prop); } // Ignore return true; }); } if (t.isAssignmentExpression(node) || t.isAssignmentPattern(node)) { if (isStatic(node.right)) { return true; } if (!t.isTSParameterProperty(node.left)) { return false; } return isStatic(node); } if (t.isSequenceExpression(node)) { return node.expressions.every(isStatic); } if (t.isConditionalExpression(node)) { return ( isStatic(node.test) || isStatic(node.consequent) || isStatic(node.alternate) ); } if (t.isBinaryExpression(node)) { if (t.isExpression(node.left)) { return isStatic(node.left); } if (t.isExpression(node.right)) { return isStatic(node.right); } return false; } if (t.isLogicalExpression(node)) { return isStatic(node.left) || isStatic(node.right); } return false; } ================================================ FILE: packages/core/babel/core/is-yielded.ts ================================================ import * as t from '@babel/types'; export function isYielded(node: t.Expression | t.SpreadElement): boolean { // Default if (t.isYieldExpression(node)) { return true; } if (t.isTemplateLiteral(node)) { return node.expressions.some( expr => t.isExpression(expr) && isYielded(expr), ); } if ( t.isLiteral(node) || t.isIdentifier(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node) || t.isClassExpression(node) || t.isYieldExpression(node) || t.isJSX(node) || t.isMetaProperty(node) || t.isSuper(node) || t.isThisExpression(node) || t.isImport(node) || t.isDoExpression(node) ) { return false; } if (t.isTaggedTemplateExpression(node)) { return isYielded(node.tag) || isYielded(node.quasi); } if ( t.isUnaryExpression(node) || t.isUpdateExpression(node) || t.isSpreadElement(node) ) { return isYielded(node.argument); } if ( t.isParenthesizedExpression(node) || t.isTypeCastExpression(node) || t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node) || t.isTSNonNullExpression(node) || t.isTSTypeAssertion(node) || t.isTSInstantiationExpression(node) ) { return isYielded(node.expression); } // Check for elements if (t.isArrayExpression(node) || t.isTupleExpression(node)) { return node.elements.some(el => el != null && isYielded(el)); } // Skip arrow function if (t.isAssignmentExpression(node)) { if (isYielded(node.right)) { return true; } if (t.isExpression(node.left)) { return isYielded(node.left); } return false; } if (t.isBinaryExpression(node)) { if (t.isExpression(node.left) && isYielded(node.left)) { return true; } return isYielded(node.right); } if ( t.isCallExpression(node) || t.isOptionalCallExpression(node) || t.isNewExpression(node) ) { if (t.isExpression(node.callee) && isYielded(node.callee)) { return true; } return node.arguments.some( arg => arg && (t.isSpreadElement(arg) || t.isExpression(arg)) && isYielded(arg), ); } if (t.isConditionalExpression(node)) { return ( isYielded(node.test) || isYielded(node.consequent) || isYielded(node.alternate) ); } if (t.isLogicalExpression(node)) { return isYielded(node.left) || isYielded(node.right); } if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) { return ( isYielded(node.object) || (node.computed && t.isExpression(node.property) && isYielded(node.property)) ); } if (t.isSequenceExpression(node)) { return node.expressions.some(isYielded); } if (t.isObjectExpression(node) || t.isRecordExpression(node)) { return node.properties.some(prop => { if (t.isObjectProperty(prop)) { if (t.isExpression(prop.value) && isYielded(prop.value)) { return true; } if (prop.computed && t.isExpression(prop.key) && isYielded(prop.key)) { return true; } return false; } if (t.isSpreadElement(prop)) { return isYielded(prop); } return false; }); } if (t.isBindExpression(node)) { return isYielded(node.object) || isYielded(node.callee); } return false; } ================================================ FILE: packages/core/babel/core/memo-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { accessorVariable } from './accessor-variable'; import { getImportIdentifier } from './get-import-identifier'; import type { State } from './types'; import { UNDEFINED } from '../constants'; export function memoVariable( state: State, path: babel.NodePath, memoIdentifier: t.Identifier, stateIdentifier: t.Expression, optionsIdentifier?: t.Expression, ): t.VariableDeclarator { const normalIdentifier = t.isArrowFunctionExpression(stateIdentifier) || t.isFunctionExpression(stateIdentifier) ? stateIdentifier : t.arrowFunctionExpression([], stateIdentifier); const exprs: t.Expression[] = [normalIdentifier]; if (state.opts.dev) { exprs.push(UNDEFINED); if (optionsIdentifier) { exprs.push( t.callExpression( t.memberExpression(t.identifier('Object'), t.identifier('assign')), [ t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(memoIdentifier.name), ), ]), optionsIdentifier, ], ), ); } else { exprs.push( t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(memoIdentifier.name), ), ]), ); } } else if (optionsIdentifier) { exprs.push(UNDEFINED); exprs.push(optionsIdentifier); } return accessorVariable( path, memoIdentifier, getImportIdentifier(state, path, 'createMemo', 'solid-js'), exprs, ); } ================================================ FILE: packages/core/babel/core/proto.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { generateUniqueName } from './generate-unique-name'; interface ProtoObjectState { root: t.ObjectExpression; proto: t.ObjectExpression; } const ROOT_GET = 'get'; const ROOT_SET = 'set'; const ROOT_SYMBOL = '__$'; const PROTO_STATES = new WeakMap(); function getProtoState( path: babel.NodePath, ): ProtoObjectState { const current = PROTO_STATES.get(path.node); if (current) { return current; } const protoID = generateUniqueName(path, 'proto'); const proto = t.objectExpression([]); path.scope.getProgramParent().push({ id: protoID, init: proto, kind: 'const', }); path.node.properties.push( t.objectProperty(t.identifier('__proto__'), protoID), ); const state: ProtoObjectState = { proto, root: path.node, }; PROTO_STATES.set(path.node, state); return state; } function getGetterReplacement( key: t.Expression, source: t.Expression, computed: boolean, ): t.ObjectMethod { return t.objectMethod( 'get', key, [], t.blockStatement([t.returnStatement(t.callExpression(source, []))]), computed, ); } function getSetterReplacement( path: babel.NodePath, key: t.Expression, source: t.Expression, computed: boolean, ): t.ObjectMethod { const param = generateUniqueName(path, 'param'); return t.objectMethod( 'set', key, [param], t.blockStatement([ t.expressionStatement( t.callExpression(source, [t.arrowFunctionExpression([], param)]), ), ]), computed, ); } function getNamespacedKey( name: string, identifier: t.Expression, ): t.Expression | undefined { switch (identifier.type) { case 'StringLiteral': case 'NumericLiteral': return t.stringLiteral(`${ROOT_SYMBOL}${name}__${identifier.value}`); case 'Identifier': return t.identifier(`${ROOT_SYMBOL}${name}__${identifier.name}`); case 'NullLiteral': return t.identifier(`${ROOT_SYMBOL}${name}__null`); default: return undefined; } } function initProtoGetters( path: babel.NodePath, identifier: t.Expression, source: t.Identifier, ): void { const current = getProtoState(path); const key = getNamespacedKey(ROOT_GET, identifier); if (key) { current.root.properties.push(t.objectProperty(key, source)); } } function registerProtoGetter( path: babel.NodePath, identifier: t.Expression, ): void { const current = getProtoState(path); const key = getNamespacedKey(ROOT_GET, identifier); if (key) { const targetProperty = t.memberExpression( t.identifier('this'), key, !t.isIdentifier(key), ); current.proto.properties.push( getGetterReplacement(identifier, targetProperty, false), ); } } function addUnoptimizedGetter( property: babel.NodePath, key: t.Expression, source: t.Identifier, ): void { property.replaceWith( getGetterReplacement(key, source, property.node.computed), ); } export function addProtoGetter( path: babel.NodePath, property: babel.NodePath, identifier: t.Expression, source: t.Identifier, ): void { if (property.node.computed) { addUnoptimizedGetter(property, identifier, source); } else { initProtoGetters(path, identifier, source); registerProtoGetter(path, identifier); property.remove(); } } function initProtoSetters( path: babel.NodePath, identifier: t.Expression, source: t.Identifier, ): void { const current = getProtoState(path); const key = getNamespacedKey(ROOT_SET, identifier); if (key) { current.root.properties.push(t.objectProperty(key, source)); } } function registerProtoSetter( path: babel.NodePath, identifier: t.Expression, ): void { const current = getProtoState(path); const key = getNamespacedKey(ROOT_SET, identifier); if (key) { const targetProperty = t.memberExpression( t.identifier('this'), key, !t.isIdentifier(key), ); current.proto.properties.push( getSetterReplacement(path, identifier, targetProperty, false), ); } } function addUnoptimizedSetter( property: babel.NodePath, key: t.Expression, source: t.Identifier, ): void { property.replaceWith( getSetterReplacement(property, key, source, property.node.computed), ); } export function addProtoSetter( path: babel.NodePath, property: babel.NodePath, identifier: t.Expression, source: t.Identifier, ): void { if (property.node.computed) { addUnoptimizedSetter(property, identifier, source); } else { initProtoSetters(path, identifier, source); registerProtoSetter(path, identifier); property.remove(); } } function addUnoptimizedProperty( property: babel.NodePath, key: t.PrivateName | t.Expression, readSource: t.Identifier, writeSource: t.Identifier, ): void { if (!t.isPrivateName(key)) { const tmp = generateUniqueName(property, 'tmp'); property.scope.push({ id: tmp, kind: 'let' }); const isComputed = property.node.computed; property.replaceWithMultiple([ getGetterReplacement( isComputed ? t.assignmentExpression('=', tmp, key) : key, readSource, isComputed, ), getSetterReplacement(property, tmp, writeSource, isComputed), ]); } } export function addProtoProperty( path: babel.NodePath, property: babel.NodePath, identifier: t.Expression, readSource: t.Identifier, writeSource: t.Identifier, ): void { if (property.node.computed) { addUnoptimizedProperty(property, identifier, readSource, writeSource); } else { initProtoGetters(path, identifier, readSource); initProtoSetters(path, identifier, writeSource); registerProtoGetter(path, identifier); registerProtoSetter(path, identifier); property.remove(); } } ================================================ FILE: packages/core/babel/core/signal-variable.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { derefSignal } from './deref-signal'; import { generateUniqueName } from './generate-unique-name'; import { getImportIdentifier } from './get-import-identifier'; import type { State } from './types'; export function signalVariable( state: State, path: babel.NodePath, signalIdentifier: t.Identifier, stateIdentifier: t.Expression, optionsIdentifier?: t.Expression, ): t.VariableDeclarator { const readIdentifier = generateUniqueName(path, signalIdentifier.name); const writeIdentifier = generateUniqueName( path, `set${signalIdentifier.name}`, ); const callee = getImportIdentifier(state, path, 'createSignal', 'solid-js'); const args: t.Expression[] = [stateIdentifier]; if (state.opts.dev) { const nameOption = t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(signalIdentifier.name), ), ]); if (optionsIdentifier) { args.push( t.callExpression( t.memberExpression(t.identifier('Object'), t.identifier('assign')), [nameOption, optionsIdentifier], ), ); } else { args.push(nameOption); } } else if (optionsIdentifier) { args.push(optionsIdentifier); } derefSignal(path, signalIdentifier, readIdentifier, writeIdentifier); return t.variableDeclarator( t.arrayPattern([readIdentifier, writeIdentifier]), t.callExpression(callee, args), ); } ================================================ FILE: packages/core/babel/core/types.ts ================================================ import type * as babel from '@babel/core'; import type * as t from '@babel/types'; export interface Options { dev?: boolean; disabled?: { ctf?: Record; pragma?: Record; label?: Record; }; } type ImportHook = Map; export interface State extends babel.PluginPass { hooks: ImportHook; opts: Options; } ================================================ FILE: packages/core/babel/core/unwrap-node.ts ================================================ import type { NodePath } from '@babel/traverse'; import type * as t from '@babel/types'; type TypeFilter = (node: t.Node) => node is K; type TypeCheck = K extends TypeFilter ? U : never; type NestedExpression = | t.ParenthesizedExpression | t.TypeCastExpression | t.TSAsExpression | t.TSSatisfiesExpression | t.TSNonNullExpression | t.TSInstantiationExpression | t.TSTypeAssertion; function isNestedExpression(node: t.Node): node is NestedExpression { switch (node.type) { case 'ParenthesizedExpression': case 'TypeCastExpression': case 'TSAsExpression': case 'TSSatisfiesExpression': case 'TSNonNullExpression': case 'TSTypeAssertion': case 'TSInstantiationExpression': return true; default: return false; } } export function isPathValid( path: unknown, key: TypeFilter, ): path is NodePath { return key((path as NodePath).node); } export function unwrapNode boolean>( node: t.Node, key: K, ): TypeCheck | undefined { if (key(node)) { return node as TypeCheck; } if (isNestedExpression(node)) { return unwrapNode(node.expression, key); } return undefined; } export function getProperParentPath boolean>( path: NodePath, key: K, ): NodePath> | undefined { let parent = path.parentPath; while (parent) { if (isNestedExpression(parent.node)) { parent = parent.parentPath; } else if (key(parent.node)) { return parent as NodePath>; } else { return undefined; } } return undefined; } ================================================ FILE: packages/core/babel/index.ts ================================================ import type * as babel from '@babel/core'; import { transformComponents } from './components'; import type { Options, State } from './core/types'; import { transformComments } from './transform-comment'; import { transformCTF } from './transform-ctf'; import { transformLabels } from './transform-label'; export type { Options }; export default function solidLabelsPlugin(): babel.PluginObj { return { name: 'solid-labels', pre(): void { this.hooks = new Map(); }, visitor: { Program(path, state): void { transformComments(state, path); transformLabels(state, path); transformCTF(state, path); transformComponents(state, path); }, }, }; } ================================================ FILE: packages/core/babel/transform-comment.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { accessorVariable } from './core/accessor-variable'; import { deferredVariable } from './core/deferred-variable'; import { destructureVariable } from './core/destructure-variable'; import { getImportIdentifier } from './core/get-import-identifier'; import { memoVariable } from './core/memo-variable'; import { signalVariable } from './core/signal-variable'; import type { State } from './core/types'; import { UNDEFINED } from './constants'; const VARIABLE_LABEL = { '@signal': true, '@memo': true, '@deferred': true, '@destructure': true, '@children': true, }; type CallbackLabel = [name: string, source: string, named: boolean]; const CALLBACK_LABEL: Record = { '@effect': ['createEffect', 'solid-js', true], '@computed': ['createComputed', 'solid-js', true], '@renderEffect': ['createRenderEffect', 'solid-js', true], '@mount': ['onMount', 'solid-js', false], '@cleanup': ['onCleanup', 'solid-js', false], '@error': ['onError', 'solid-js', false], '@root': ['createRoot', 'solid-js', false], '@untrack': ['untrack', 'solid-js', false], '@batch': ['untrack', 'solid-js', false], '@transition': ['startTransition', 'solid-js', false], }; function getVariableLabelPreference( state: State, comments: t.Comment[], ): string | undefined { let preference: string | undefined; for (let i = 0, len = comments.length; i < len; i++) { const comment = comments[i]; const value: string = comment.value.trim(); if (value in VARIABLE_LABEL && !state.opts.disabled?.pragma?.[value]) { preference = value; comment.value = ''; } } return preference; } const LABEL_PATTERN = /^@\w+( .*)?$/; function getCallbackLabelPreference(state: State, comments: t.Comment[]) { let preference: string | undefined; let nameOption: string | undefined; for (let i = 0, len = comments.length; i < len; i++) { const comment = comments[i]; const value: string = comment.value.trim(); if (LABEL_PATTERN.test(value)) { const [tag, ...debugName] = value.split(' '); if (tag in CALLBACK_LABEL && !state.opts.disabled?.pragma?.[value]) { preference = tag; nameOption = debugName.join(' '); comment.value = ''; } } } return [preference, nameOption]; } const COMMENT_TRAVERSE: babel.Visitor = { VariableDeclaration(path, state) { const comments = path.node.leadingComments; if (comments) { const preference = getVariableLabelPreference(state, comments); if (preference) { const { declarations } = path.node; let declarators: t.VariableDeclarator[] = []; for (let i = 0, len = declarations.length; i < len; i++) { const declarator = declarations[i]; switch (preference as keyof typeof VARIABLE_LABEL) { case '@signal': if (t.isIdentifier(declarator.id)) { declarators.push( signalVariable( state, path, declarator.id, declarator.init ?? UNDEFINED, ), ); } break; case '@memo': if (t.isIdentifier(declarator.id)) { declarators.push( memoVariable( state, path, declarator.id, declarator.init ?? UNDEFINED, ), ); } break; case '@deferred': if (t.isIdentifier(declarator.id)) { declarators.push( deferredVariable( state, path, declarator.id, declarator.init ?? UNDEFINED, ), ); } break; case '@destructure': if ( (t.isObjectPattern(declarator.id) || t.isArrayPattern(declarator.id)) && declarator.init ) { declarators = [ ...declarators, ...destructureVariable( state, path, declarator.init, declarator.id, ), ]; } break; case '@children': if (t.isIdentifier(declarator.id)) { declarators.push( accessorVariable( path, declarator.id, getImportIdentifier(state, path, 'children', 'solid-js'), [ t.arrowFunctionExpression( [], declarator.init ?? UNDEFINED, ), ], ), ); } break; default: break; } } path.replaceWith(t.variableDeclaration('const', declarators)); } } }, BlockStatement(path, state) { if (!t.isBlockStatement(path.parent)) { return; } const comments = path.node.leadingComments; if (comments) { const [preference, nameOption] = getCallbackLabelPreference( state, comments, ); if (preference) { const [name, source, named] = CALLBACK_LABEL[preference]; const callback = t.arrowFunctionExpression([], path.node); const args: t.Expression[] = [callback]; if (named && nameOption) { args.push( t.unaryExpression('void', t.numericLiteral(0)), t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(nameOption), ), ]), ); } path.replaceWith( t.callExpression( getImportIdentifier(state, path, name, source), args, ), ); } } }, ExpressionStatement(path, state) { const comments = path.node.leadingComments; if (comments) { const [preference, nameOption] = getCallbackLabelPreference( state, comments, ); if (preference) { const [name, source, named] = CALLBACK_LABEL[preference]; const callback = t.arrowFunctionExpression([], path.node.expression); const args: t.Expression[] = [callback]; if (named && nameOption) { args.push( UNDEFINED, t.objectExpression([ t.objectProperty( t.identifier('name'), t.stringLiteral(nameOption), ), ]), ); } path.replaceWith( t.callExpression( getImportIdentifier(state, path, name, source), args, ), ); } } }, }; export function transformComments(state: State, path: babel.NodePath): void { path.traverse(COMMENT_TRAVERSE, state); } ================================================ FILE: packages/core/babel/transform-ctf.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { accessorVariable } from './core/accessor-variable'; import { assert } from './core/assert'; import { deferredVariable } from './core/deferred-variable'; import { derefMemoVariable } from './core/deref-memo-variable'; import { derefSignalVariable } from './core/deref-signal-variable'; import { destructureVariable } from './core/destructure-variable'; import { unexpectedArgumentLength, unexpectedType } from './core/errors'; import { generateUniqueName } from './core/generate-unique-name'; import { getImportIdentifier } from './core/get-import-identifier'; import { memoVariable } from './core/memo-variable'; import { signalVariable } from './core/signal-variable'; import type { State } from './core/types'; import { unwrapNode } from './core/unwrap-node'; import { UNDEFINED } from './constants'; type AutoArrowCTF = [name: string, source: string, arguments: number]; const AUTO_ARROW_CTF: Record = { $lazy: ['lazy', 'solid-js', 1], $untrack: ['untrack', 'solid-js', 1], $batch: ['batch', 'solid-js', 1], $observable: ['observable', 'solid-js', 1], $selector: ['createSelector', 'solid-js', 1], $on: ['on', 'solid-js', 3], }; type AutoImportAliasCTF = [name: string, source: string]; const AUTO_IMPORT_ALIAS_CTF: Record = { $root: ['createRoot', 'solid-js'], $useContext: ['useContext', 'solid-js'], $createContext: ['createContext', 'solid-js'], $uid: ['createUniqueId', 'solid-js'], $effect: ['createEffect', 'solid-js'], $computed: ['createComputed', 'solid-js'], $renderEffect: ['createRenderEffect', 'solid-js'], $merge: ['mergeProps', 'solid-js'], $resource: ['createResource', 'solid-js'], $cleanup: ['onCleanup', 'solid-js'], $mount: ['onMount', 'solid-js'], $error: ['onError', 'solid-js'], $reaction: ['createReaction', 'solid-js'], $startTransition: ['startTransition', 'solid-js'], $useTransition: ['useTransition', 'solid-js'], $owner: ['getOwner', 'solid-js'], $runWithOwner: ['runWithOwner', 'solid-js'], $catchError: ['catchError', 'solid-js'], // Store API $store: ['createStore', 'solid-js/store'], $mutable: ['createMutable', 'solid-js/store'], $produce: ['produce', 'solid-js/store'], $reconcile: ['reconcile', 'solid-js/store'], $unwrap: ['unwrap', 'solid-js/store'], }; type AutoAccessorCTF = [ name: string, source: string, args: number, arrow: boolean, ]; const AUTO_ACCESSOR_CTF: Record = { $from: ['from', 'solid-js', 1, true], $children: ['children', 'solid-js', 1, false], $mapArray: ['mapArray', 'solid-js', 3, false], $indexArray: ['indexArray', 'solid-js', 3, false], }; const SIGNAL_CTF = '$signal'; const MEMO_CTF = '$memo'; const DEREF_SIGNAL_CTF = '$derefSignal'; const DEREF_MEMO_CTF = '$derefMemo'; const REACTIVE_CTF = '$'; const DEFERRED_CTF = '$deferred'; const DESTRUCTURE_CTF = '$destructure'; const COMPONENT_CTF = '$component'; const SPECIAL_CTF = new Set([ SIGNAL_CTF, MEMO_CTF, DEREF_SIGNAL_CTF, DEREF_MEMO_CTF, REACTIVE_CTF, DEFERRED_CTF, DESTRUCTURE_CTF, COMPONENT_CTF, ]); const CTF_TRAVERSE: babel.Visitor = { CallExpression(path, state) { const trueIdentifier = unwrapNode(path.node.callee, t.isIdentifier); if ( trueIdentifier && !path.scope.hasBinding(trueIdentifier.name) && !state.opts.disabled?.ctf?.[trueIdentifier.name] ) { // Transform Auto Arrow CTFs if (trueIdentifier.name in AUTO_ARROW_CTF) { const [name, source, limit] = AUTO_ARROW_CTF[trueIdentifier.name]; const args = path.node.arguments; assert( args.length <= limit, unexpectedArgumentLength(path, args.length, limit), ); const [argument, ...rest] = args; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); path.replaceWith( t.callExpression(getImportIdentifier(state, path, name, source), [ t.isArrowFunctionExpression(argument) || t.isFunctionExpression(argument) ? argument : t.arrowFunctionExpression([], argument), ...rest, ]), ); } if (trueIdentifier.name === COMPONENT_CTF) { const args = path.node.arguments; assert( args.length === 1, unexpectedArgumentLength(path, args.length, 1), ); const argument = args[0]; assert( t.isFunctionExpression(argument) || t.isArrowFunctionExpression(argument), unexpectedType( path, argument.type, 'FunctionExpression | ArrowFunctionExpression', ), ); if (argument.params.length > 0) { const params = argument.params[0]; if (t.isObjectPattern(params)) { // Generate uid for props const props = generateUniqueName(path, 'props'); // Replace params with props argument.params[0] = props; const declaration = t.variableDeclaration('const', [ t.variableDeclarator( params, t.callExpression(t.identifier('$destructure'), [props]), ), ]); if (t.isExpression(argument.body)) { argument.body = t.blockStatement([ declaration, t.returnStatement(argument.body), ]); } else { argument.body.body.unshift(declaration); } } } path.replaceWith(argument); } } }, Expression(path, state) { if (t.isIdentifier(path.node) && !path.scope.hasBinding(path.node.name)) { if ( path.node.name in AUTO_IMPORT_ALIAS_CTF && !state.opts.disabled?.ctf?.[path.node.name] ) { const [name, source] = AUTO_IMPORT_ALIAS_CTF[path.node.name]; path.replaceWith(getImportIdentifier(state, path, name, source)); } } }, VariableDeclarator(path, state) { const { id, init } = path.node; if (init) { const trueCallExpr = unwrapNode(init, t.isCallExpression); if (trueCallExpr) { const trueCallee = unwrapNode(trueCallExpr.callee, t.isIdentifier); if ( trueCallee && !path.scope.hasBinding(trueCallee.name) && (SPECIAL_CTF.has(trueCallee.name) || trueCallee.name in AUTO_ACCESSOR_CTF) && !state.opts.disabled?.ctf?.[trueCallee.name] ) { if (t.isIdentifier(id)) { // Transform CTFs with auto-accessor if (trueCallee.name in AUTO_ACCESSOR_CTF) { const [name, source, limit, arrow] = AUTO_ACCESSOR_CTF[trueCallee.name]; const args = trueCallExpr.arguments; assert( args.length <= limit, unexpectedArgumentLength(path, args.length, limit), ); const [argument, ...rest] = args; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); path.replaceWith( accessorVariable( path, id, getImportIdentifier(state, path, name, source), [ arrow ? t.arrowFunctionExpression([], argument) : argument, ...rest, ], ), ); } else if (trueCallee.name === SIGNAL_CTF) { // Transform $signal const args = trueCallExpr.arguments; assert( args.length <= 2, unexpectedArgumentLength(path, args.length, 2), ); let argument: t.Expression | undefined; let options: t.Expression | undefined; if (trueCallExpr.arguments.length > 0) { const initialState = trueCallExpr.arguments[0]; assert( t.isExpression(initialState), unexpectedType(path, initialState.type, 'Expression'), ); argument = initialState; if (trueCallExpr.arguments.length > 1) { const optionsValue = trueCallExpr.arguments[1]; assert( t.isExpression(optionsValue), unexpectedType(path, optionsValue.type, 'Expression'), ); options = optionsValue; } } path.replaceWith( signalVariable(state, path, id, argument || UNDEFINED, options), ); } else if ( trueCallee.name === MEMO_CTF || trueCallee.name === REACTIVE_CTF ) { // Transform $memo const args = trueCallExpr.arguments; assert( args.length <= 2, unexpectedArgumentLength(path, args.length, 2), ); const argument = args[0]; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); let options: t.Expression | undefined; if (args.length > 1) { const optionsValue = args[1]; assert( t.isExpression(optionsValue), unexpectedType(path, optionsValue.type, 'Expression'), ); options = optionsValue; } path.replaceWith( memoVariable(state, path, id, argument, options), ); } else if (trueCallee.name === DEREF_SIGNAL_CTF) { // Transform $derefSignal const args = trueCallExpr.arguments; assert( args.length === 1, unexpectedArgumentLength(path, args.length, 1), ); const argument = args[0]; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); path.replaceWith(derefSignalVariable(path, id, argument)); } else if (trueCallee.name === DEREF_MEMO_CTF) { // Transform $derefMemo const args = trueCallExpr.arguments; assert( args.length === 1, unexpectedArgumentLength(path, args.length, 1), ); const argument = args[0]; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); path.replaceWith(derefMemoVariable(path, id, argument)); } else if (trueCallee.name === DEFERRED_CTF) { // Transform $deferred const args = trueCallExpr.arguments; assert( args.length <= 2, unexpectedArgumentLength(path, args.length, 2), ); let argument: t.Expression | undefined; let options: t.Expression | undefined; if (args.length > 0) { const initialState = args[0]; assert( t.isExpression(initialState), unexpectedType(path, initialState.type, 'Expression'), ); argument = initialState; if (args.length > 1) { const optionsValue = args[1]; assert( t.isExpression(optionsValue), unexpectedType(path, optionsValue.type, 'Expression'), ); options = optionsValue; } } path.replaceWith( deferredVariable(state, path, id, argument, options), ); } } else if (trueCallee.name === DESTRUCTURE_CTF) { const args = trueCallExpr.arguments; assert( args.length === 1, unexpectedArgumentLength(path, args.length, 1), ); const argument = args[0]; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); assert( t.isObjectPattern(id) || t.isArrayPattern(id), unexpectedType(path, id.type, 'ArrayPattern | ObjectPattern'), ); path.replaceWithMultiple( destructureVariable(state, path, argument, id), ); } path.scope.crawl(); } } } }, ExpressionStatement(path, state) { const trueCallExpr = unwrapNode(path.node.expression, t.isCallExpression); if (trueCallExpr) { const trueCallee = unwrapNode(trueCallExpr.callee, t.isIdentifier); if ( trueCallee && !path.scope.hasBinding(trueCallee.name) && !state.opts.disabled?.ctf?.[trueCallee.name] ) { // Transform $ if (trueCallee.name === REACTIVE_CTF) { const args = trueCallExpr.arguments; assert( args.length === 1, unexpectedArgumentLength(path, args.length, 1), ); const argument = args[0]; assert( t.isExpression(argument), unexpectedType(path, argument.type, 'Expression'), ); path.replaceWith( t.expressionStatement( t.callExpression( getImportIdentifier(state, path, 'createEffect', 'solid-js'), [ t.isArrowFunctionExpression(argument) || t.isFunctionExpression(argument) ? argument : t.arrowFunctionExpression([], argument), ], ), ), ); } } } }, }; export function transformCTF(state: State, path: babel.NodePath): void { path.traverse(CTF_TRAVERSE, state); } ================================================ FILE: packages/core/babel/transform-label.ts ================================================ import type * as babel from '@babel/core'; import * as t from '@babel/types'; import { accessorVariable } from './core/accessor-variable'; import { assert } from './core/assert'; import { deferredVariable } from './core/deferred-variable'; import { destructureVariable } from './core/destructure-variable'; import { unexpectedType } from './core/errors'; import { getImportIdentifier } from './core/get-import-identifier'; import { memoVariable } from './core/memo-variable'; import { signalVariable } from './core/signal-variable'; import type { State } from './core/types'; import { UNDEFINED } from './constants'; const REACTIVE_LABEL = '$'; const SPECIAL_LABELS = new Set([REACTIVE_LABEL]); const VARIABLE_LABEL = { signal: true, memo: true, deferred: true, destructure: true, children: true, }; type CallbackLabel = [name: string, source: string, named: boolean]; const CALLBACK_LABEL: Record = { effect: ['createEffect', 'solid-js', true], computed: ['createComputed', 'solid-js', true], renderEffect: ['createRenderEffect', 'solid-js', true], mount: ['onMount', 'solid-js', false], cleanup: ['onCleanup', 'solid-js', false], error: ['onError', 'solid-js', false], root: ['createRoot', 'solid-js', false], untrack: ['untrack', 'solid-js', false], batch: ['untrack', 'solid-js', false], transition: ['startTransition', 'solid-js', false], }; function transformReactiveLabel( state: State, path: babel.NodePath, body: t.Statement, ): void { let target: t.Expression | t.BlockStatement; if (t.isExpressionStatement(body)) { target = body.expression; } else if (t.isBlockStatement(body)) { target = body; } else { throw unexpectedType( path, body.type, 'ExpressionStatement | BlockStatement', ); } path.replaceWith( t.callExpression( getImportIdentifier(state, path, 'createEffect', 'solid-js'), [t.arrowFunctionExpression([], target)], ), ); } function transformDeclaratorFromVariableLabel( state: State, path: babel.NodePath, labelName: keyof typeof VARIABLE_LABEL, declarator: t.VariableDeclarator, ): t.VariableDeclarator[] { if (labelName === 'signal' && t.isIdentifier(declarator.id)) { return [ signalVariable(state, path, declarator.id, declarator.init ?? UNDEFINED), ]; } if (labelName === 'memo' && t.isIdentifier(declarator.id)) { return [ memoVariable(state, path, declarator.id, declarator.init ?? UNDEFINED), ]; } if (labelName === 'deferred' && t.isIdentifier(declarator.id)) { return [ deferredVariable( state, path, declarator.id, declarator.init ?? UNDEFINED, ), ]; } if ( labelName === 'destructure' && (t.isObjectPattern(declarator.id) || t.isArrayPattern(declarator.id)) && declarator.init ) { return destructureVariable(state, path, declarator.init, declarator.id); } if (labelName === 'children' && t.isIdentifier(declarator.id)) { return [ accessorVariable( path, declarator.id, getImportIdentifier(state, path, 'children', 'solid-js'), [t.arrowFunctionExpression([], declarator.init ?? UNDEFINED)], ), ]; } return []; } function transformVariableLabel( state: State, path: babel.NodePath, labelName: keyof typeof VARIABLE_LABEL, body: t.Statement, ): void { assert( t.isVariableDeclaration(body), unexpectedType(path, path.node.type, 'VariableDeclaration'), ); const declarators: t.VariableDeclarator[] = []; for (let i = 0, len = body.declarations.length; i < len; i++) { declarators.push.apply( declarators, transformDeclaratorFromVariableLabel( state, path, labelName, body.declarations[i], ), ); } path.replaceWith(t.variableDeclaration('const', declarators)); } function transformCallbackLabel( state: State, path: babel.NodePath, labelName: string, body: t.Statement, ): void { const [name, source, named] = CALLBACK_LABEL[labelName]; let nameOption: string | undefined; let callback: t.Expression; let currentBody = body; if (named && t.isLabeledStatement(currentBody)) { nameOption = currentBody.label.name; currentBody = currentBody.body; } if (t.isBlockStatement(currentBody)) { callback = t.arrowFunctionExpression([], currentBody); } else if (t.isExpressionStatement(currentBody)) { callback = currentBody.expression; } else { throw unexpectedType( path, currentBody.type, 'BlockStatement | ExpressionStatement', ); } const args: t.Expression[] = [callback]; if (named && nameOption) { args.push( UNDEFINED, t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral(nameOption)), ]), ); } path.replaceWith( t.callExpression(getImportIdentifier(state, path, name, source), args), ); } const LABEL_TRAVERSE: babel.Visitor = { LabeledStatement(path, state) { const labelName = path.node.label.name; const { body } = path.node; if ( (SPECIAL_LABELS.has(labelName) || labelName in VARIABLE_LABEL || labelName in CALLBACK_LABEL) && !state.opts.disabled?.label?.[labelName] ) { if (labelName === REACTIVE_LABEL) { transformReactiveLabel(state, path, body); } else if (labelName in VARIABLE_LABEL) { transformVariableLabel( state, path, labelName as keyof typeof VARIABLE_LABEL, body, ); } else if (labelName in CALLBACK_LABEL) { transformCallbackLabel(state, path, labelName, body); } } }, }; export function transformLabels(state: State, path: babel.NodePath): void { path.traverse(LABEL_TRAVERSE, state); } ================================================ FILE: packages/core/example.js ================================================ import * as babel from '@babel/core'; import plugin from './dist/esm/development/babel.mjs'; async function compile(code, dev) { const result = await babel.transformAsync(code, { plugins: [[plugin, { dev }]], parserOpts: { plugins: ['jsx'], }, }); return result?.code ?? ''; } console.log( await compile(` let foo = $signal('foo'); let bar = $signal('bar') let baz = $memo('baz'); const example = { foo: $property(foo), [baz]: $property(bar), baz: baz, }; $(console.log(example.foo, example.bar)); `), ); ================================================ FILE: packages/core/package.json ================================================ { "version": "0.17.0", "type": "module", "exports": { ".": { "development": { "require": "./dist/cjs/development/index.cjs", "import": "./dist/esm/development/index.mjs" }, "require": "./dist/cjs/production/index.cjs", "import": "./dist/esm/production/index.mjs", "types": "./dist/types/src/index.d.ts" }, "./babel": { "development": { "require": "./dist/cjs/development/babel.cjs", "import": "./dist/esm/development/babel.mjs" }, "require": "./dist/cjs/production/babel.cjs", "import": "./dist/esm/production/babel.mjs", "types": "./dist/types/babel/index.d.ts" } }, "files": [ "dist", "src" ], "engines": { "node": ">=10" }, "license": "MIT", "keywords": [ "pridepack", "babel", "solid-js", "labels", "reactivity" ], "name": "solid-labels", "devDependencies": { "@babel/core": "^7.26.9", "@types/babel__core": "^7.20.5", "@types/babel__traverse": "^7.20.6", "@types/node": "^22.13.4", "pridepack": "2.6.1", "solid-js": "^1.9.3", "tslib": "^2.8.1", "typescript": "^5.8.2", "vitest": "^3.0.6" }, "peerDependencies": { "@babel/core": "^7.25", "solid-js": "^1.3" }, "dependencies": { "@babel/traverse": "^7.26.9", "@babel/types": "^7.26.9" }, "scripts": { "prepublish": "pridepack clean && pridepack build", "build": "pridepack build", "type-check": "pridepack check", "lint": "pridepack lint", "clean": "pridepack clean", "watch": "pridepack watch", "test": "vitest" }, "description": "Simple, reactive labels for SolidJS", "repository": { "url": "https://github.com/lxsmnsyc/solid-labels.git", "type": "git" }, "homepage": "https://github.com/lxsmnsyc/solid-labels/tree/main/packages/core", "bugs": { "url": "https://github.com/lxsmnsyc/solid-labels/issues" }, "publishConfig": { "access": "public" }, "author": "Alexis Munsayac", "private": false, "typesVersions": { "*": { "babel": [ "./dist/types/babel/index.d.ts" ] } }, "types": "./dist/types/src/index.d.ts", "main": "./dist/cjs/production/index.cjs", "module": "./dist/esm/production/index.mjs" } ================================================ FILE: packages/core/pridepack.json ================================================ { "target": "es2018", "entrypoints": { ".": "src/index.ts", "./babel": "babel/index.ts" } } ================================================ FILE: packages/core/src/index.ts ================================================ import type * as solid from 'solid-js'; import type * as solidStore from 'solid-js/store'; import type * as solidWeb from 'solid-js/web'; declare global { type Accessor = solid.Accessor; type Setter = solid.Setter; type Context = solid.Context; type ObservableObserver = solid.ObservableObserver; type Component = {}> = solid.Component; function $signal(): T | undefined; function $signal( value: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; internal?: boolean; }, ): T; function $memo( value: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; }, ): T; function $memo( value: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; }, ): T; function $untrack(value: T): T; function $batch(value: T): T; function $(value: T): T; function $derefSignal(value: [Accessor, Setter]): T; function $refSignal(value: T): [Accessor, Setter]; function $derefMemo(value: Accessor): T; function $refMemo(value: T): Accessor; function $get(value: T): Accessor; function $set(value: T): Setter; // Object property transforms function $getter(value: T): T; function $setter(value: T): T; function $property(value: T): T; function $selector( source: T, fn?: (a: U, b: T) => boolean, options?: { name?: string }, ): (key: U) => boolean; function $on( deps: T, fn: (input: T, prevInput: T, prevValue?: U) => U, options?: { defer?: boolean }, ): (prevValue?: U) => U; function $deferred( source: T, options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; timeoutMs?: number; }, ): T; function $lazy>( fn: Promise<{ default: T }>, ): T & { preload: () => void; }; function $children(value: solid.JSX.Element): solid.JSX.Element; interface Observable { subscribe( observer: ObservableObserver, ): { unsubscribe(): void } | (() => void); } function $observable(value: T): Observable; function $from(observable: Observable): T; function $from(produce: (setter: Setter) => () => void): T; function $mapArray( arr: readonly T[] | undefined | null | false, mapFn: (v: T, i: Accessor) => U, options?: { fallback?: Accessor; }, ): U[]; function $indexArray( arr: readonly T[] | undefined | null | false, mapFn: (v: Accessor, i: number) => U, options?: { fallback?: Accessor; }, ): U[]; function $destructure(value: T): T; // Auto imports const $effect: typeof solid.createEffect; const $computed: typeof solid.createComputed; const $renderEffect: typeof solid.createRenderEffect; const $useContext: typeof solid.useContext; const $createContext: typeof solid.createContext; const $uid: typeof solid.createUniqueId; const $root: typeof solid.createRoot; const $resource: typeof solid.createResource; const $merge: typeof solid.mergeProps; const $reaction: typeof solid.createReaction; const $mount: typeof solid.onMount; // @deprecated const $error: typeof solid.onError; const $cleanup: typeof solid.onCleanup; const $catchError: typeof solid.catchError; const $startTransition: typeof solid.startTransition; const $useTransition: typeof solid.useTransition; const $owner: typeof solid.getOwner; const $runWithOwner: typeof solid.runWithOwner; // store const $store: typeof solidStore.createStore; const $mutable: typeof solidStore.createMutable; const $produce: typeof solidStore.produce; const $reconcile: typeof solidStore.reconcile; const $unwrap: typeof solidStore.unwrap; // components const For: typeof solid.For; const Show: typeof solid.Show; const Switch: typeof solid.Switch; const Match: typeof solid.Match; const Index: typeof solid.Index; const ErrorBoundary: typeof solid.ErrorBoundary; const Suspense: typeof solid.Suspense; const SuspenseList: typeof solid.SuspenseList; const Dynamic: typeof solidWeb.Dynamic; const Portal: typeof solidWeb.Portal; const Assets: typeof solidWeb.Assets; const HydrationScript: typeof solidWeb.HydrationScript; const NoHydration: typeof solidWeb.NoHydration; function $component

( Comp: (props: P) => solid.JSX.Element, ): (props: P) => solid.JSX.Element; } type Props = T extends (props: infer P) => solid.JSX.Element ? P : never; declare module 'solid-js' { // biome-ignore lint/style/noNamespace: namespace JSX { interface IntrinsicElements { 'solid:error-boundary': Props; 'solid:suspense': Props; 'solid:suspense-list': Props; 'solid:portal': Props; 'solid:assets': Props; 'solid:hydration-script': Props; 'solid:no-hydration': Props; } } } ================================================ FILE: packages/core/test/__snapshots__/ctf.test.ts.snap ================================================ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`ctf > $component > should transform $component 1`] = ` "import { splitProps as _splitProps } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; };" `; exports[`ctf > $component > should transform $component bindings 1`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; _createEffect(() => { console.log(prop_2(), prop_3()); }); _createEffect(() => { console.log(prop_5(), prop_6()); }); _createEffect(() => { console.log(other_1); }); };" `; exports[`ctf > $component > should transform $component bindings 2`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; _createEffect(() => { console.log({ b: prop_2() }, { c: prop_3() }); }); _createEffect(() => { console.log({ d: prop_5() }, { e: prop_6() }); }); _createEffect(() => { console.log(other_1); }); };" `; exports[`ctf > $component > should transform $component bindings on $get 1`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; _createEffect(() => { console.log(prop_2, prop_3); }); _createEffect(() => { console.log(prop_5, prop_6); }); _createEffect(() => { console.log(other_1); }); };" `; exports[`ctf > $component > should transform $component bindings on $getter 1`] = ` "const proto_1 = { get b() { return this.__$get__b(); } }, proto_2 = { get c() { return this.__$get__c(); } }, proto_3 = { get d() { return this.__$get__d(); } }, proto_4 = { get e() { return this.__$get__e(); } }; import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; _createEffect(() => { console.log({ __proto__: proto_1, __$get__b: prop_2 }, { __proto__: proto_2, __$get__c: prop_3 }); }); _createEffect(() => { console.log({ __proto__: proto_3, __$get__d: prop_5 }, { __proto__: proto_4, __$get__e: prop_6 }); }); _createEffect(() => { console.log(other_1); }); };" `; exports[`ctf > $component > should transform $component bindings on $property 1`] = ` "const proto_1 = { get b() { return this.__$get__b(); } }, proto_2 = { get c() { return this.__$get__c(); } }, proto_3 = { get d() { return this.__$get__d(); } }, proto_4 = { get e() { return this.__$get__e(); } }; import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; _createEffect(() => { console.log({ __proto__: proto_1, __$get__b: prop_2 }, { __proto__: proto_2, __$get__c: prop_3 }); }); _createEffect(() => { console.log({ __proto__: proto_3, __$get__d: prop_5 }, { __proto__: proto_4, __$get__e: prop_6 }); }); _createEffect(() => { console.log(other_1); }); };" `; exports[`ctf > $component > should transform $component bindings on $refMemo 1`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; props_1 => { const prop_1 = () => props_1.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => props_1.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(props_1, ["a", "b"])[1]; _createEffect(() => { console.log(prop_2, prop_3); }); _createEffect(() => { console.log(prop_5, prop_6); }); _createEffect(() => { console.log(other_1); }); };" `; exports[`ctf > $derefMemo > should transform $derefMemo 1`] = `"const message_1 = () => \`Count: \${count}\`;"`; exports[`ctf > $derefMemo > should transform $derefMemo bindings 1`] = ` "const message_1 = () => \`Count: \${count}\`; const value = message_1();" `; exports[`ctf > $derefMemo > should transform $derefMemo bindings 2`] = ` "const message_1 = () => \`Count: \${count}\`; const value = { message: message_1() };" `; exports[`ctf > $derefMemo > should transform $derefMemo on $get 1`] = ` "const message_1 = () => \`Count: \${count}\`; const value = message_1;" `; exports[`ctf > $derefMemo > should transform $derefMemo on $getter 1`] = ` "const proto_1 = { get message() { return this.__$get__message(); } }; const message_1 = () => \`Count: \${count}\`; const value = { __proto__: proto_1, __$get__message: message_1 };" `; exports[`ctf > $derefMemo > should transform $derefMemo on $property 1`] = ` "const proto_1 = { get message() { return this.__$get__message(); } }; const message_1 = () => \`Count: \${count}\`; const value = { __proto__: proto_1, __$get__message: message_1 };" `; exports[`ctf > $derefMemo > should transform $derefMemo on $refMemo 1`] = ` "const message_1 = () => \`Count: \${count}\`; const value = message_1;" `; exports[`ctf > $derefSignal > should transform $derefSignal 1`] = `"const [count_1, setcount_1] = createSignal(0);"`; exports[`ctf > $derefSignal > should transform $derefSignal bindings 1`] = ` "const [count_1, setcount_1] = createSignal(0); const value = count_1();" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings 2`] = ` "const [count_1, setcount_1] = createSignal(0); const value = { count: count_1() };" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings for $get 1`] = ` "const [count_1, setcount_1] = createSignal(0); const value = count_1;" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings for $getter 1`] = ` "const proto_1 = { get count() { return this.__$get__count(); } }; const [count_1, setcount_1] = createSignal(0); const value = { __proto__: proto_1, __$get__count: setcount_1 };" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings for $property 1`] = ` "const proto_1 = { get count() { return this.__$get__count(); }, set count(param_1) { this.__$set__count(() => param_1); } }; const [count_1, setcount_1] = createSignal(0); const value = { __proto__: proto_1, __$get__count: count_1, __$set__count: setcount_1 };" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings for $refSignal 1`] = ` "const [count_1, setcount_1] = createSignal(0); const value = [count_1, setcount_1];" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings for $set 1`] = ` "const [count_1, setcount_1] = createSignal(0); const value = setcount_1;" `; exports[`ctf > $derefSignal > should transform $derefSignal bindings for $setter 1`] = ` "const proto_1 = { set count(param_1) { this.__$set__count(() => param_1); } }; const [count_1, setcount_1] = createSignal(0); const value = { __proto__: proto_1, __$set__count: setcount_1 };" `; exports[`ctf > $destructure > should transform $destructure 1`] = ` "import { splitProps as _splitProps } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1];" `; exports[`ctf > $destructure > should transform $destructure bindings 1`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log(prop_2(), prop_3()); }); _createEffect(() => { console.log(prop_5(), prop_6()); }); _createEffect(() => { console.log(other_1); });" `; exports[`ctf > $destructure > should transform $destructure bindings 2`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log({ b: prop_2() }, { c: prop_3() }); }); _createEffect(() => { console.log({ d: prop_5() }, { e: prop_6() }); }); _createEffect(() => { console.log(other_1); });" `; exports[`ctf > $destructure > should transform $destructure bindings on $get 1`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log(prop_2, prop_3); }); _createEffect(() => { console.log(prop_5, prop_6); }); _createEffect(() => { console.log(other_1); });" `; exports[`ctf > $destructure > should transform $destructure bindings on $getter 1`] = ` "const proto_1 = { get b() { return this.__$get__b(); } }, proto_2 = { get c() { return this.__$get__c(); } }, proto_3 = { get d() { return this.__$get__d(); } }, proto_4 = { get e() { return this.__$get__e(); } }; import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log({ __proto__: proto_1, __$get__b: prop_2 }, { __proto__: proto_2, __$get__c: prop_3 }); }); _createEffect(() => { console.log({ __proto__: proto_3, __$get__d: prop_5 }, { __proto__: proto_4, __$get__e: prop_6 }); }); _createEffect(() => { console.log(other_1); });" `; exports[`ctf > $destructure > should transform $destructure bindings on $property 1`] = ` "const proto_1 = { get b() { return this.__$get__b(); } }, proto_2 = { get c() { return this.__$get__c(); } }, proto_3 = { get d() { return this.__$get__d(); } }, proto_4 = { get e() { return this.__$get__e(); } }; import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log({ __proto__: proto_1, __$get__b: prop_2 }, { __proto__: proto_2, __$get__c: prop_3 }); }); _createEffect(() => { console.log({ __proto__: proto_3, __$get__d: prop_5 }, { __proto__: proto_4, __$get__e: prop_6 }); }); _createEffect(() => { console.log(other_1); });" `; exports[`ctf > $destructure > should transform $destructure bindings on $refMemo 1`] = ` "import { splitProps as _splitProps } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; let prop_1 = () => x.a, prop_2 = () => prop_1().b, prop_3 = () => prop_1().c, other_2 = _splitProps(prop_1(), ["b", "c"])[1], prop_4 = () => x.b, def_1 = defaultD, prop_5 = () => { const value_1 = prop_4().d; return value_1 === void 0 ? def_1 : value_1; }, def_2 = defaultE, prop_6 = () => { const value_2 = prop_4().e; return value_2 === void 0 ? def_2 : value_2; }, other_3 = _splitProps(prop_4(), ["d", "e"])[1], other_1 = _splitProps(x, ["a", "b"])[1]; _createEffect(() => { console.log(prop_2, prop_3); }); _createEffect(() => { console.log(prop_5, prop_6); }); _createEffect(() => { console.log(other_1); });" `; exports[`ctf > $effect, $renderEffect, $computed > should transform $effect, $renderEffect, $computed 1`] = ` "import { createRenderEffect as _createRenderEffect } from "solid-js"; import { createComputed as _createComputed } from "solid-js"; import { createEffect as _createEffect } from "solid-js"; import { createSignal as _createSignal } from "solid-js"; let [x_1, setx_1] = _createSignal(0); _createEffect(() => { console.log('Count', x_1()); }); _createComputed(() => { console.log('Count', x_1()); }); _createRenderEffect(() => { console.log('Count', x_1()); });" `; exports[`ctf > $memo > should transform $memo 1`] = ` "import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`);" `; exports[`ctf > $memo > should transform $memo bindings 1`] = ` "import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`); const value = message_1();" `; exports[`ctf > $memo > should transform $memo bindings 2`] = ` "import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`); const value = { message: message_1() };" `; exports[`ctf > $memo > should transform $memo on $get 1`] = ` "import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`); const value = message_1;" `; exports[`ctf > $memo > should transform $memo on $getter 1`] = ` "const proto_1 = { get message() { return this.__$get__message(); } }; import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`); const value = { __proto__: proto_1, __$get__message: message_1 };" `; exports[`ctf > $memo > should transform $memo on $property 1`] = ` "const proto_1 = { get message() { return this.__$get__message(); } }; import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`); const value = { __proto__: proto_1, __$get__message: message_1 };" `; exports[`ctf > $memo > should transform $memo on $refMemo 1`] = ` "import { createMemo as _createMemo } from "solid-js"; const message_1 = _createMemo(() => \`Count: \${count}\`); const value = message_1;" `; exports[`ctf > $signal > should transform $signal 1`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0);" `; exports[`ctf > $signal > should transform $signal bindings 1`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = count_1();" `; exports[`ctf > $signal > should transform $signal bindings 2`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = { count: count_1() };" `; exports[`ctf > $signal > should transform $signal bindings 3`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); async function exampleA() { const tmp_1 = await asyncValue(); setcount_1(() => tmp_1); } function* exampleB() { const tmp_2 = yield asyncValue(); setcount_1(() => tmp_2); } async function* exampleC() { const tmp_3 = yield asyncValue(); setcount_1(() => tmp_3); } const example = async () => { const tmp_4 = await asyncValue(); return setcount_1(() => tmp_4); };" `; exports[`ctf > $signal > should transform $signal bindings for $get 1`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = count_1;" `; exports[`ctf > $signal > should transform $signal bindings for $getter 1`] = ` "const proto_1 = { get count() { return this.__$get__count(); } }; import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = { __proto__: proto_1, __$get__count: setcount_1 };" `; exports[`ctf > $signal > should transform $signal bindings for $property 1`] = ` "const proto_1 = { get count() { return this.__$get__count(); }, set count(param_1) { this.__$set__count(() => param_1); } }; import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = { __proto__: proto_1, __$get__count: count_1, __$set__count: setcount_1 };" `; exports[`ctf > $signal > should transform $signal bindings for $refSignal 1`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = [count_1, setcount_1];" `; exports[`ctf > $signal > should transform $signal bindings for $set 1`] = ` "import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = setcount_1;" `; exports[`ctf > $signal > should transform $signal bindings for $setter 1`] = ` "const proto_1 = { set count(param_1) { this.__$set__count(() => param_1); } }; import { createSignal as _createSignal } from "solid-js"; let [count_1, setcount_1] = _createSignal(0); const value = { __proto__: proto_1, __$set__count: setcount_1 };" `; exports[`ctf > variable shadowing > should respect variable shadowing 1`] = ` "import { createSignal as _createSignal } from "solid-js"; const [selected_1, setselected_1] = _createSignal('root'); { const selected = 'local'; console.log(selected); // 'local' { console.log(selected); // 'local' { const [selected_2, setselected_2] = _createSignal('inner'); console.log(selected_2()); // 'inner' { console.log(selected_2()); // 'inner' } } } }" `; ================================================ FILE: packages/core/test/ctf.test.ts ================================================ import * as babel from '@babel/core'; import { describe, expect, it } from 'vitest'; import plugin from '../babel'; async function compile(code: string, dev?: boolean): Promise { const result = await babel.transformAsync(code, { plugins: [[plugin, { dev }]], parserOpts: { plugins: ['jsx'], }, }); return result?.code ?? ''; } describe('ctf', () => { describe('$signal', () => { it('should transform $signal', async () => { expect(await compile('let count = $signal(0);')).toMatchSnapshot(); }); it('should transform $signal bindings', async () => { let code = ` let count = $signal(0); const value = count; `; expect(await compile(code)).toMatchSnapshot(); code = ` let count = $signal(0); const value = { count }; `; expect(await compile(code)).toMatchSnapshot(); code = ` let count = $signal(0); async function exampleA() { count = await asyncValue(); } function* exampleB() { count = yield asyncValue(); } async function* exampleC() { count = yield asyncValue(); } const example = async () => (count = await asyncValue()); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $signal bindings for $set', async () => { const code = ` let count = $signal(0); const value = $set(count); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $signal bindings for $get', async () => { const code = ` let count = $signal(0); const value = $get(count); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $signal bindings for $refSignal', async () => { const code = ` let count = $signal(0); const value = $refSignal(count); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $signal bindings for $getter', async () => { const code = ` let count = $signal(0); const value = { count: $getter(count) }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $signal bindings for $setter', async () => { const code = ` let count = $signal(0); const value = { count: $setter(count) }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $signal bindings for $property', async () => { const code = ` let count = $signal(0); const value = { count: $property(count) }; `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('$memo', () => { it('should transform $memo', async () => { const code = 'const message = $memo(`Count: ${count}`);'; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $memo bindings', async () => { let code = ` const message = $memo(\`Count: \${count}\`) const value = message; `; expect(await compile(code)).toMatchSnapshot(); code = ` const message = $memo(\`Count: \${count}\`) const value = { message }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $memo on $get', async () => { const code = ` const message = $memo(\`Count: \${count}\`) const value = $get(message); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $memo on $refMemo', async () => { const code = ` const message = $memo(\`Count: \${count}\`) const value = $refMemo(message); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $memo on $getter', async () => { const code = ` const message = $memo(\`Count: \${count}\`) const value = { message: $getter(message) }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $memo on $property', async () => { const code = ` const message = $memo(\`Count: \${count}\`) const value = { message: $property(message) }; `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('$derefSignal', () => { it('should transform $derefSignal', async () => { const code = ` const count = $derefSignal(createSignal(0)); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings', async () => { let code = ` const count = $derefSignal(createSignal(0)); const value = count; `; expect(await compile(code)).toMatchSnapshot(); code = ` const count = $derefSignal(createSignal(0)); const value = { count }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings for $set', async () => { const code = ` const count = $derefSignal(createSignal(0)); const value = $set(count); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings for $get', async () => { const code = ` const count = $derefSignal(createSignal(0)); const value = $get(count); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings for $refSignal', async () => { const code = ` const count = $derefSignal(createSignal(0)); const value = $refSignal(count); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings for $getter', async () => { const code = ` const count = $derefSignal(createSignal(0)); const value = { count: $getter(count) }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings for $setter', async () => { const code = ` const count = $derefSignal(createSignal(0)); const value = { count: $setter(count) }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefSignal bindings for $property', async () => { const code = ` const count = $derefSignal(createSignal(0)); const value = { count: $property(count) }; `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('$derefMemo', () => { it('should transform $derefMemo', async () => { const code = ` const message = $derefMemo(() => \`Count: \${count}\`); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefMemo bindings', async () => { let code = ` const message = $derefMemo(() => \`Count: \${count}\`); const value = message; `; expect(await compile(code)).toMatchSnapshot(); code = ` const message = $derefMemo(() => \`Count: \${count}\`); const value = { message }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefMemo on $get', async () => { const code = ` const message = $derefMemo(() => \`Count: \${count}\`); const value = $get(message); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefMemo on $refMemo', async () => { const code = ` const message = $derefMemo(() => \`Count: \${count}\`); const value = $refMemo(message); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefMemo on $getter', async () => { const code = ` const message = $derefMemo(() => \`Count: \${count}\`); const value = { message: $getter(message) }; `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $derefMemo on $property', async () => { const code = ` const message = $derefMemo(() => \`Count: \${count}\`); const value = { message: $property(message) }; `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('$destructure', () => { it('should transform $destructure', async () => { const code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $destructure bindings', async () => { let code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log(b, c); } effect: { console.log(d, e); } effect: { console.log(f); } `; expect(await compile(code)).toMatchSnapshot(); code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log({ b }, { c }); } effect: { console.log({ d }, { e }); } effect: { console.log(f); } `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $destructure bindings on $get', async () => { const code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log($get(b), $get(c)); } effect: { console.log($get(d), $get(e)); } effect: { console.log(f); } `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $destructure bindings on $refMemo', async () => { const code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log($refMemo(b), $refMemo(c)); } effect: { console.log($refMemo(d), $refMemo(e)); } effect: { console.log(f); } `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $destructure bindings on $getter', async () => { const code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log({ b: $getter(b) }, { c: $getter(c) }); } effect: { console.log({ d: $getter(d) }, { e: $getter(e) }); } effect: { console.log(f); } `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $destructure bindings on $property', async () => { const code = ` let { a: { b, c }, b: { d = defaultD, e = defaultE }, ...f } = $destructure(x); effect: { console.log({ b: $property(b) }, { c: $property(c) }); } effect: { console.log({ d: $property(d) }, { e: $property(e) }); } effect: { console.log(f); } `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('$component', () => { it('should transform $component', async () => { const code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { }); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $component bindings', async () => { let code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { effect: { console.log(b, c); } effect: { console.log(d, e); } effect: { console.log(f); } }); `; expect(await compile(code)).toMatchSnapshot(); code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { effect: { console.log({ b }, { c }); } effect: { console.log({ d }, { e }); } effect: { console.log(f); } }); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $component bindings on $get', async () => { const code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { effect: { console.log($get(b), $get(c)); } effect: { console.log($get(d), $get(e)); } effect: { console.log(f); } }); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $component bindings on $refMemo', async () => { const code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { effect: { console.log($refMemo(b), $refMemo(c)); } effect: { console.log($refMemo(d), $refMemo(e)); } effect: { console.log(f); } }); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $component bindings on $getter', async () => { const code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { effect: { console.log({ b: $getter(b) }, { c: $getter(c) }); } effect: { console.log({ d: $getter(d) }, { e: $getter(e) }); } effect: { console.log(f); } }); `; expect(await compile(code)).toMatchSnapshot(); }); it('should transform $component bindings on $property', async () => { const code = ` $component(({ a: { b, c }, b: { d = defaultD, e = defaultE }, ...f }) => { effect: { console.log({ b: $property(b) }, { c: $property(c) }); } effect: { console.log({ d: $property(d) }, { e: $property(e) }); } effect: { console.log(f); } }); `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('$effect, $renderEffect, $computed', () => { it('should transform $effect, $renderEffect, $computed', async () => { const code = ` let x = $signal(0); $effect(() => { console.log('Count', x); }); $computed(() => { console.log('Count', x); }); $renderEffect(() => { console.log('Count', x); }); `; expect(await compile(code)).toMatchSnapshot(); }); }); describe('variable shadowing', () => { it('should respect variable shadowing', async () => { const code = ` const selected = $signal('root'); { const selected = 'local'; console.log(selected); // 'local' { console.log(selected); // 'local' { const selected = $signal('inner'); console.log(selected); // 'inner' { console.log(selected); // 'inner' } } } } `; expect(await compile(code)).toMatchSnapshot(); }); }); }); ================================================ FILE: packages/core/tsconfig.json ================================================ { "exclude": ["node_modules"], "include": ["src", "types", "babel"], "compilerOptions": { "module": "ESNext", "lib": ["ESNext", "DOM"], "importHelpers": true, "declaration": true, "sourceMap": true, "rootDir": "./", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "Bundler", "jsx": "react", "esModuleInterop": true, "target": "ES2017", "useDefineForClassFields": false, "declarationMap": true } } ================================================ FILE: packages/rollup/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.production .env.development # parcel-bundler cache (https://parceljs.org/) .cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and *not* Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port .npmrc ================================================ FILE: packages/rollup/README.md ================================================ # rollup-plugin-solid-labels > Rollup plugin for [`solid-labels`](https://github.com/lxsmnsyc/solid-labels) [![NPM](https://img.shields.io/npm/v/rollup-plugin-solid-labels.svg)](https://www.npmjs.com/package/rollup-plugin-solid-labels) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript) ## Install ```bash npm install --D solid-labels rollup-plugin-solid-labels ``` ```bash yarn add -D solid-labels rollup-plugin-solid-labels ``` ```bash pnpm add -D solid-labels rollup-plugin-solid-labels ``` ## Usage ```js import solidLabels from 'rollup-plugin-solid-labels'; ///... solidLabels({ dev: true, // defaults to false disabled: { labels: { signal: true, }, pragma: { '@signal': true, }, ctf: { $signal: true, }, }, filter: { include: 'src/**/*.{ts,js,tsx,jsx}', exclude: 'node_modules/**/*.{ts,js,tsx,jsx}', }, }) ``` > [!INFO] > When you are using a SolidJS Rollup plugin, make sure that solid-labels runs first. ## Sponsors ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) ## License MIT © [lxsmnsyc](https://github.com/lxsmnsyc) ================================================ FILE: packages/rollup/package.json ================================================ { "version": "0.17.0", "type": "module", "types": "./dist/types/index.d.ts", "main": "./dist/cjs/production/index.cjs", "module": "./dist/esm/production/index.mjs", "exports": { ".": { "development": { "require": "./dist/cjs/development/index.cjs", "import": "./dist/esm/development/index.mjs" }, "require": "./dist/cjs/production/index.cjs", "import": "./dist/esm/production/index.mjs", "types": "./dist/types/index.d.ts" } }, "files": [ "dist", "src" ], "engines": { "node": ">=10" }, "license": "MIT", "keywords": [ "pridepack", "babel", "solid-js", "labels", "reactivity" ], "name": "rollup-plugin-solid-labels", "devDependencies": { "@types/node": "^22.13.4", "pridepack": "2.6.1", "rollup": "^4.24.0", "tslib": "^2.8.1", "typescript": "^5.8.2" }, "dependencies": { "unplugin-solid-labels": "0.17.0" }, "peerDependencies": { "rollup": "^3 || ^4", "solid-labels": "^0.16" }, "scripts": { "prepublish": "pridepack clean && pridepack build", "build": "pridepack build", "type-check": "pridepack check", "lint": "pridepack lint", "clean": "pridepack clean", "watch": "pridepack watch" }, "description": "Simple, reactive labels for SolidJS", "repository": { "url": "https://github.com/lxsmnsyc/solid-labels.git", "type": "git" }, "homepage": "https://github.com/lxsmnsyc/solid-labels/tree/main/packages/vite", "bugs": { "url": "https://github.com/lxsmnsyc/solid-labels/issues" }, "publishConfig": { "access": "public" }, "author": "Alexis Munsayac", "private": false, "typesVersions": { "*": {} } } ================================================ FILE: packages/rollup/pridepack.json ================================================ { "target": "es2018" } ================================================ FILE: packages/rollup/src/index.ts ================================================ import type { SolidLabelsPluginOptions } from 'unplugin-solid-labels'; import solidLabelsUnplugin from 'unplugin-solid-labels'; import type { Plugin } from 'rollup'; export type { SolidLabelsPluginFilter, SolidLabelsPluginOptions, } from 'unplugin-solid-labels'; const solidLabelsPlugin = solidLabelsUnplugin.rollup as ( options: SolidLabelsPluginOptions, ) => Plugin; export default solidLabelsPlugin; ================================================ FILE: packages/rollup/tsconfig.json ================================================ { "exclude": ["node_modules"], "include": ["src", "types"], "compilerOptions": { "module": "ESNext", "lib": ["ESNext", "DOM"], "importHelpers": true, "declaration": true, "sourceMap": true, "rootDir": "./src", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "Bundler", "jsx": "react", "esModuleInterop": true, "target": "ES2017", "useDefineForClassFields": false, "declarationMap": true } } ================================================ FILE: packages/unplugin/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.production .env.development # parcel-bundler cache (https://parceljs.org/) .cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and *not* Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port .npmrc ================================================ FILE: packages/unplugin/README.md ================================================ # unplugin-solid-labels > [Unplugin](https://github.com/unjs/unplugin) for [`solid-labels`](https://github.com/lxsmnsyc/solid-labels) [![NPM](https://img.shields.io/npm/v/unplugin-solid-labels.svg)](https://www.npmjs.com/package/unplugin-solid-labels) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript) ## Install ```bash npm install --D solid-labels unplugin-solid-labels ``` ```bash yarn add -D solid-labels unplugin-solid-labels ``` ```bash pnpm add -D solid-labels unplugin-solid-labels ``` ## Usage ```js import solidLabels from 'unplugin-solid-labels'; ///... solidLabels.vite({ dev: true, // defaults to false disabled: { labels: { signal: true, }, pragma: { '@signal': true, }, ctf: { $signal: true, }, }, filter: { include: 'src/**/*.{ts,js,tsx,jsx}', exclude: 'node_modules/**/*.{ts,js,tsx,jsx}', }, }) ``` ## Sponsors ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) ## License MIT © [lxsmnsyc](https://github.com/lxsmnsyc) ================================================ FILE: packages/unplugin/package.json ================================================ { "name": "unplugin-solid-labels", "version": "0.17.0", "type": "module", "files": [ "dist", "babel", "core" ], "engines": { "node": ">=10" }, "license": "MIT", "keywords": [ "pridepack" ], "devDependencies": { "@types/babel__core": "^7.20.5", "@types/node": "^22.13.4", "pridepack": "2.6.1", "rollup": "^4.34.8", "solid-labels": "0.17.0", "tslib": "^2.8.1", "typescript": "^5.8.2", "vite": "^6.1.1" }, "dependencies": { "@babel/core": "^7.26.9", "@rollup/pluginutils": "^5.1.4", "unplugin": "^2.2.0" }, "peerDependencies": { "rollup": "^3 || 4", "solid-labels": "^0.16", "vite": "^3 || ^4 || ^5" }, "peerDependenciesMeta": { "vite": { "optional": true }, "rollup": { "optional": true } }, "scripts": { "prepublishOnly": "pridepack clean && pridepack build", "build": "pridepack build", "type-check": "pridepack check", "lint": "pridepack lint", "clean": "pridepack clean", "watch": "pridepack watch", "start": "pridepack start", "dev": "pridepack dev" }, "description": "Rollup plugin for solid-styled", "repository": { "url": "https://github.com/lxsmnsyc/solid-styled.git", "type": "git" }, "homepage": "https://github.com/lxsmnsyc/solid-styled/packages/rollup", "bugs": { "url": "https://github.com/lxsmnsyc/solid-styled/issues" }, "publishConfig": { "access": "public" }, "author": "Alexis Munsayac", "private": false, "types": "./dist/types/index.d.ts", "main": "./dist/cjs/production/index.cjs", "module": "./dist/esm/production/index.mjs", "exports": { ".": { "development": { "require": "./dist/cjs/development/index.cjs", "import": "./dist/esm/development/index.mjs" }, "require": "./dist/cjs/production/index.cjs", "import": "./dist/esm/production/index.mjs", "types": "./dist/types/index.d.ts" } }, "typesVersions": { "*": {} } } ================================================ FILE: packages/unplugin/pridepack.json ================================================ { "target": "es2018" } ================================================ FILE: packages/unplugin/src/index.ts ================================================ import * as babel from '@babel/core'; import type { FilterPattern } from '@rollup/pluginutils'; import { createFilter } from '@rollup/pluginutils'; import path from 'node:path'; import type { Options } from 'solid-labels/babel'; import solidLabelsBabel from 'solid-labels/babel'; import type { TransformResult } from 'unplugin'; import { createUnplugin } from 'unplugin'; import type { Plugin } from 'vite'; export interface SolidLabelsPluginFilter { include?: FilterPattern; exclude?: FilterPattern; } export interface SolidLabelsPluginOptions extends Options { filter?: SolidLabelsPluginFilter; } // From: https://github.com/bluwy/whyframe/blob/master/packages/jsx/src/index.js#L27-L37 function repushPlugin( plugins: Plugin[], pluginName: string, pluginNames: string[], ): void { const namesSet = new Set(pluginNames); let baseIndex = -1; let targetIndex = -1; let targetPlugin: Plugin | undefined; for (let i = 0, len = plugins.length; i < len; i += 1) { const current = plugins[i]; if (namesSet.has(current.name) && baseIndex === -1) { baseIndex = i; } if (current.name === pluginName) { targetIndex = i; targetPlugin = current; } } if ( targetPlugin && baseIndex !== -1 && targetIndex !== -1 && baseIndex < targetIndex ) { plugins.splice(targetIndex, 1); plugins.splice(baseIndex, 0, targetPlugin); } } const DEFAULT_INCLUDE = 'src/**/*.{jsx,tsx,ts,js,mjs,cjs}'; const DEFAULT_EXCLUDE = 'node_modules/**/*.{jsx,tsx,ts,js,mjs,cjs}'; const FILE_FILTER = /\.[mc]?tsx?$/i; const solidLabelsPlugin = createUnplugin( (options: SolidLabelsPluginOptions = {}) => { const filter = createFilter( options.filter?.include || DEFAULT_INCLUDE, options.filter?.exclude || DEFAULT_EXCLUDE, ); let env: string; return { name: 'solid-labels', transformInclude(id): boolean { return filter(id); }, async transform(code, id): Promise { const plugins: NonNullable< NonNullable['plugins'] > = ['jsx']; if (FILE_FILTER.test(id)) { plugins.push('typescript'); } const result = await babel.transformAsync(code, { plugins: [ [ solidLabelsBabel, { disabled: options.disabled, dev: env === 'development' || options.dev, }, ], ], parserOpts: { plugins, }, filename: path.basename(id), ast: false, sourceMaps: true, configFile: false, babelrc: false, sourceFileName: id, }); if (result) { return { code: result.code || '', map: result.map, }; } return undefined; }, vite: { enforce: 'pre', configResolved(config): void { env = config.mode !== 'production' ? 'development' : 'production'; // run our plugin before the following plugins: repushPlugin(config.plugins as Plugin[], 'solid-labels', [ // https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-jsx/index.ts#L173 'astro:jsx', // https://github.com/solidjs/vite-plugin-solid/blob/master/src/index.ts#L305 'solid', // https://github.com/solidjs/solid-start/blob/main/packages/start/vite/plugin.js#L118 'solid-start-file-system-router', ]); }, }, }; }, ); export default solidLabelsPlugin; ================================================ FILE: packages/unplugin/tsconfig.json ================================================ { "exclude": ["node_modules"], "include": ["src"], "compilerOptions": { "module": "ESNext", "lib": ["ESNext", "DOM"], "importHelpers": true, "declaration": true, "sourceMap": true, "rootDir": "./src", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "Bundler", "jsx": "react", "esModuleInterop": true, "target": "ES2017", "useDefineForClassFields": false, "declarationMap": true } } ================================================ FILE: packages/vite/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.production .env.development # parcel-bundler cache (https://parceljs.org/) .cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and *not* Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port .npmrc ================================================ FILE: packages/vite/README.md ================================================ # vite-plugin-solid-labels > Vite plugin for [`solid-labels`](https://github.com/lxsmnsyc/solid-labels) [![NPM](https://img.shields.io/npm/v/vite-plugin-solid-labels.svg)](https://www.npmjs.com/package/vite-plugin-solid-labels) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript) ## Install ```bash npm install --D solid-labels vite-plugin-solid-labels ``` ```bash yarn add -D solid-labels vite-plugin-solid-labels ``` ```bash pnpm add -D solid-labels vite-plugin-solid-labels ``` ## Usage ```js import solidLabels from 'vite-plugin-solid-labels'; ///... solidLabels({ dev: true, // defaults to false disabled: { labels: { signal: true, }, pragma: { '@signal': true, }, ctf: { $signal: true, }, }, filter: { include: 'src/**/*.{ts,js,tsx,jsx}', exclude: 'node_modules/**/*.{ts,js,tsx,jsx}', }, }) ``` ## Sponsors ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) ## License MIT © [lxsmnsyc](https://github.com/lxsmnsyc) ================================================ FILE: packages/vite/package.json ================================================ { "version": "0.17.0", "type": "module", "types": "./dist/types/index.d.ts", "main": "./dist/cjs/production/index.cjs", "module": "./dist/esm/production/index.mjs", "exports": { ".": { "development": { "require": "./dist/cjs/development/index.cjs", "import": "./dist/esm/development/index.mjs" }, "require": "./dist/cjs/production/index.cjs", "import": "./dist/esm/production/index.mjs", "types": "./dist/types/index.d.ts" } }, "files": [ "dist", "src" ], "engines": { "node": ">=10" }, "license": "MIT", "keywords": [ "pridepack", "babel", "solid-js", "labels", "reactivity" ], "name": "vite-plugin-solid-labels", "devDependencies": { "@types/node": "^22.13.4", "pridepack": "2.6.0", "rollup": "^4.34.8", "solid-labels": "0.17.0", "tslib": "^2.8.1", "typescript": "^5.8.2", "vite": "^6.1.1" }, "dependencies": { "unplugin-solid-labels": "0.17.0" }, "peerDependencies": { "solid-labels": "^0.16", "vite": "^3 || ^4 || ^5" }, "scripts": { "prepublish": "pridepack clean && pridepack build", "build": "pridepack build", "type-check": "pridepack check", "lint": "pridepack lint", "clean": "pridepack clean", "watch": "pridepack watch" }, "description": "Simple, reactive labels for SolidJS", "repository": { "url": "https://github.com/lxsmnsyc/solid-labels.git", "type": "git" }, "homepage": "https://github.com/lxsmnsyc/solid-labels/tree/main/packages/vite", "bugs": { "url": "https://github.com/lxsmnsyc/solid-labels/issues" }, "publishConfig": { "access": "public" }, "author": "Alexis Munsayac", "private": false, "typesVersions": { "*": {} } } ================================================ FILE: packages/vite/pridepack.json ================================================ { "target": "es2018" } ================================================ FILE: packages/vite/src/index.ts ================================================ import type { SolidLabelsPluginOptions } from 'unplugin-solid-labels'; import solidLabelsUnplugin from 'unplugin-solid-labels'; import type { Plugin } from 'vite'; export type { SolidLabelsPluginFilter, SolidLabelsPluginOptions, } from 'unplugin-solid-labels'; const solidLabelsPlugin = solidLabelsUnplugin.vite as ( options: SolidLabelsPluginOptions, ) => Plugin; export default solidLabelsPlugin; ================================================ FILE: packages/vite/tsconfig.json ================================================ { "exclude": ["node_modules"], "include": ["src", "types"], "compilerOptions": { "module": "ESNext", "lib": ["ESNext", "DOM"], "importHelpers": true, "declaration": true, "sourceMap": true, "rootDir": "./src", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "Bundler", "jsx": "react", "esModuleInterop": true, "target": "ES2017", "useDefineForClassFields": false, "declarationMap": true } } ================================================ FILE: pnpm-workspace.yaml ================================================ packages: - 'packages/**/*' - 'examples/**/*'