Skip to content

Commit

Permalink
implemented deref types (#141)
Browse files Browse the repository at this point in the history
* implemented deref types

* fix crazy typescript build times

* fix skip rust script
  • Loading branch information
XantreDev committed Jun 2, 2024
1 parent 5166499 commit b29dbc5
Show file tree
Hide file tree
Showing 16 changed files with 901 additions and 765 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-meals-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@preact-signals/utils": minor
---

Disallow reexports from macro lib
5 changes: 5 additions & 0 deletions .changeset/cyan-cooks-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@preact-signals/utils": minor
---

Added `$deref` feature for state macroses to get actual state macro reference
2 changes: 0 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ jobs:
with:
workspaces: "packages/react/swc"
- run: pnpm build
env:
SKIP_RUST: "true"
- run: pnpm test
lint:
name: Lint
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dist-ssr

.turbo
tsconfig.tsbuildinfo
tsconfig.*.tsbuildinfo
vite.config.ts.timestamp-*
vitest.config.ts.timestamp-*

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"scripts": {
"build": "turbo run build --filter=./packages/*",
"build:with-apps": "turbo run build",
"build:with-playground": "turbo run build --filter=./packages/* --filter=./apps/transformer-playground",
"watch": "turbo run watch --parallel",
"test": "turbo run test --parallel",
"eslint": "eslint --ext .ts,.tsx,.js,.jsx,.cjs,.mjs . --cache --cache-location=node_modules/.eslintcache",
Expand Down
4 changes: 2 additions & 2 deletions packages/react/build.nu
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def main [

cd ./swc

let $use_skip_rust = --skip-rust == "true" or $env.SKIP_RUST? == "true"
if $use_skip_rust == "true" {
let use_skip_rust = --skip-rust == "true" or $env.SKIP_RUST? == "true"
if $use_skip_rust == true {
return
}
let $use_debug = --cargo-debug == "true" or $env.CARGO_DEBUG? == "true"
Expand Down
1 change: 1 addition & 0 deletions packages/utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ Allowed state macros:
- `$useLinkedState`
- `$derived`
- `$useDerived`
- `$deref`

Here is example of how it works. You can play with it [here](https://preact-signals.netlify.app/?transformerConfig=N4Igxg9gtgDhB2BTeAXAziAXAMwIYBs1EAaEKXMAJwixUoFdEBfIA&code=JYWwDg9gTgLgBAbzgEgM4wIYwKYBoUAm2UwAbtgXAL5wBmUEIcA5AAJhTYYDGMAtKmABzAHYYANqgD0AVxjBJUkDwbMAUKEixEcbLVrZe1OgyZsOXXlMGiJqPpx4x1a8dnjos2OAF4UnnAAKBDU4OAwALjgARgAGXFC4ACMogCZYtSoASjU1bggRdDgiEnJKP2QSsgpAgOwAOgw4AGo4OvqknLU9A15AwKzfAD5ERPzCiDd68QghQIAiKrL5-CWKHOzc9oxm5qA)

Expand Down
4 changes: 2 additions & 2 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"clean": "rimraf dist",
"clean:docs": "rimraf docs",
"test": "vitest",
"lint": "tsc --noEmit --emitDeclarationOnly false && check-export-map && node ./import-checker.mjs",
"lint": "tsc -p ./tsconfig.lint.json && tsc -p ./tsconfig.node.json --noEmit --emitDeclarationOnly false && check-export-map && node ./import-checker.mjs",
"build": "pnpm clean && rollup -c && pnpm build:types",
"build:types": "tsc",
"build:docs": "pnpm clean:docs && node ./build-typedoc.mjs",
Expand Down Expand Up @@ -160,7 +160,7 @@
}
},
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/core": "^7.24.0",
"@babel/helper-module-imports": "7.24.3",
"@babel/traverse": "^7.24.5",
"@preact-signals/unified-signals": "workspace:*",
Expand Down
14 changes: 14 additions & 0 deletions packages/utils/src/__tests__/__snapshots__/babel.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ const _ = () => {
"
`;

exports[`@preact-signals/utils/macro > Allows to export types 1`] = `
"export type * from "@preact-signals/utils/macro";
export type { a } from "@preact-signals/utils/macro";
export { type a } from "@preact-signals/utils/macro";
"
`;

exports[`@preact-signals/utils/macro > CJS import 1`] = `
"var _$ = require("@preact-signals/utils").$;
const a = _$(() => 1);
Expand Down Expand Up @@ -50,6 +57,13 @@ const _ = () => {
"
`;

exports[`@preact-signals/utils/macro > Deref is working 1`] = `
"import { deepSignal as _deepSignal } from "@preact-signals/utils";
let a = _deepSignal(10);
const aSig = a;
"
`;

exports[`@preact-signals/utils/macro > ESM import 1`] = `
"import { $ as _$ } from "@preact-signals/utils";
const a = _$(() => 1);
Expand Down
107 changes: 79 additions & 28 deletions packages/utils/src/__tests__/babel.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
import { it, describe } from "vitest";
import { it, describe, Test } from "vitest";
import { format as _format } from "prettier";
import { transform } from "@babel/core";
import preactSignalsUtilsBabel, {
BabelMacroPluginOptions,
SyntaxErrorWithLoc,
} from "../babel";

const format = (code: string) => _format(code, { parser: "acorn" });

type TestCase = {
type: "success" | "error";
name: string;
input: string;
isCJS: boolean;
usePresetEnv: boolean;
typescript: boolean;
options: BabelMacroPluginOptions | undefined;
};

const transformFromOptions = (testCase: TestCase) =>
transform(testCase.input, {
presets: testCase.usePresetEnv ? presetEnvConfig : undefined,
parserOpts: testCase.typescript
? {
plugins: ["typescript"],
}
: undefined,
plugins: [[preactSignalsUtilsBabel, testCase.options]],
sourceType: testCase.isCJS ? "script" : "module",
})?.code!;

const transformAndFormatFromOptions = (testCase: TestCase) =>
_format(transformFromOptions(testCase), {
parser: testCase.typescript ? "babel-ts" : "acorn",
});

const TestCase = {
makeSuccess: (name: string, input: string): TestCase =>
TestCase.makeConfigurable(name, input, {}),
makeSuccessTypescript: (name: string, input: string): TestCase =>
TestCase.makeConfigurable(name, input, { typescript: true }),
makeConfigurable: (
name: string,
input: string,
Expand All @@ -28,6 +47,7 @@ const TestCase = {
name,
input,
isCJS: params.isCJS ?? false,
typescript: params.typescript ?? false,
options: params.options ?? {
experimental_stateMacros: true,
},
Expand Down Expand Up @@ -221,7 +241,7 @@ describe.concurrent("@preact-signals/utils/macro", () => {
`
),
TestCase.makeSuccess(
"State macro inside of ref macro",
"State macro inside of ref macro",
`
import { $$, $state } from '@preact-signals/utils/macro'
Expand All @@ -230,14 +250,32 @@ describe.concurrent("@preact-signals/utils/macro", () => {
$$(a)
`
),
TestCase.makeSuccess("Ref macro inside of state macro",
TestCase.makeSuccess(
"Ref macro inside of state macro",
`
import { $$, $state } from '@preact-signals/utils/macro'
let a = $state(10)
let b = $state($$(a))
`
),
TestCase.makeSuccess(
"Deref is working",
`
import { $state, $deref } from '@preact-signals/utils/macro'
let a = $state(10)
const aSig = $deref(a)
`
),
TestCase.makeSuccessTypescript(
"Allows to export types",
`
export type * from '@preact-signals/utils/macro'
export type { a } from '@preact-signals/utils/macro'
export { type a } from '@preact-signals/utils/macro'
`
),
TestCase.makeConfigurable(
"Should transform by preset-env correctly",
`
Expand All @@ -252,17 +290,9 @@ describe.concurrent("@preact-signals/utils/macro", () => {
),
];

for (const { input, isCJS, name, options, usePresetEnv } of success) {
it(name, async ({ expect }) => {
expect(
await format(
transform(input, {
presets: usePresetEnv ? presetEnvConfig : undefined,
plugins: [[preactSignalsUtilsBabel, options]],
sourceType: isCJS ? "script" : "module",
})?.code!
)
).toMatchSnapshot();
for (const testCase of success) {
it(testCase.name, async ({ expect }) => {
expect(await transformAndFormatFromOptions(testCase)).toMatchSnapshot();
});
}
const fail = [
Expand Down Expand Up @@ -385,6 +415,26 @@ describe.concurrent("@preact-signals/utils/macro", () => {
export let a = $state(0)
`
),
TestCase.makeError(
"Should throw if trying to export macro",
`
import { $state } from "@preact-signals/utils/macro";
export { $state }
`
),
TestCase.makeError(
"Should throw on reexport (all)",
`
export * from "@preact-signals/utils/macro";
`
),
TestCase.makeError(
"Should throw on reexport (one)",
`
export { $state } from "@preact-signals/utils/macro";
`
),
TestCase.makeError(
"Throws if top level macro exported from module (statement export)",
`
Expand Down Expand Up @@ -432,21 +482,22 @@ describe.concurrent("@preact-signals/utils/macro", () => {
a = 10
`
),

TestCase.makeError(
"It must be imposible to deref regular var",
`
import { $state, $deref } from "@preact-signals/utils/macro";
const a = 10
const b = $deref(a)
`
),
];

for (const { input, isCJS, name, options, usePresetEnv } of fail) {
it(name, async ({ expect }) => {
for (const testCase of fail) {
it(testCase.name, async ({ expect }) => {
expect(() => {
try {
transform(input, {
presets: usePresetEnv ? presetEnvConfig : undefined,
plugins: [[preactSignalsUtilsBabel, options]],
sourceType: isCJS ? "script" : "module",
});
} catch (e) {
// console.log(e);
throw e;
}
transformFromOptions(testCase);
// @ts-expect-error private constructor is a shit show
}).toThrowError(SyntaxErrorWithLoc);
});
Expand Down
54 changes: 54 additions & 0 deletions packages/utils/src/__tests__/macro.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expectTypeOf, it } from "vitest";
import * as macro from "../lib/macro";
import { ReadonlySignal } from "@preact/signals-core";

describe("macros types", () => {
it("$deref state types", () => {
() => {
expectTypeOf(() => {
let a = macro.$state(10);

return macro.$deref(a);
}).returns.toEqualTypeOf<ReadonlySignal<10>>();

expectTypeOf(() => {
let a = macro.$useState(10);

return macro.$deref(a);
}).returns.toEqualTypeOf<ReadonlySignal<10>>();
};
});

it("$deref readonly state types", () => {
() => {
expectTypeOf(() => {
let a = macro.$derived(10);

return macro.$deref(a);
}).returns.toEqualTypeOf<ReadonlySignal<10>>();

expectTypeOf(() => {
let a = macro.$useLinkedState(10);

return macro.$deref(a);
}).returns.toEqualTypeOf<ReadonlySignal<10>>();

expectTypeOf(() => {
let a = macro.$useDerived(10);

return macro.$deref(a);
}).returns.toEqualTypeOf<ReadonlySignal<10>>();
};
});

it("$deref shouldn't work with incorrect types", () => {
() => {
{
const a = 10;

// @ts-expect-error
return macro.$deref(a);
}
};
});
});
Loading

0 comments on commit b29dbc5

Please sign in to comment.