@primate/angular 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +1 -2
- package/lib/private/client/app.js +4 -1
- package/lib/private/client/form.d.ts +10 -4
- package/lib/private/client/form.js +22 -10
- package/lib/private/create-root.d.ts +1 -1
- package/lib/private/create-root.js +4 -29
- package/lib/private/default.d.ts +1 -1
- package/lib/private/default.js +25 -35
- package/lib/private/i18n.d.ts +3 -0
- package/lib/private/i18n.js +51 -0
- package/lib/private/init.js +1 -1
- package/lib/private/root-selector.d.ts +1 -1
- package/lib/private/runtime.d.ts +1 -1
- package/lib/private/signal-inputs.d.ts +2 -0
- package/lib/private/signal-inputs.js +190 -0
- package/lib/public/browser.d.ts +5 -1
- package/lib/public/browser.js +7 -1
- package/lib/public/client.d.ts +0 -1
- package/lib/public/i18n.d.ts +2 -0
- package/lib/public/i18n.js +2 -0
- package/package.json +18 -16
package/lib/index.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export default class AngularApp {
|
|
|
9
9
|
static #app;
|
|
10
10
|
static #root;
|
|
11
11
|
static async mount(_view, data) {
|
|
12
|
+
const interactive = !data.ssr || data.csr;
|
|
13
|
+
if (!interactive)
|
|
14
|
+
return;
|
|
12
15
|
if (data.mode === "production")
|
|
13
16
|
enableProdMode();
|
|
14
17
|
const providers = [];
|
|
@@ -30,7 +33,7 @@ export default class AngularApp {
|
|
|
30
33
|
this.#root = this.#app.components[0];
|
|
31
34
|
this.#root.instance.p = props;
|
|
32
35
|
this.#app.tick();
|
|
33
|
-
if (data.
|
|
36
|
+
if (data.csr) {
|
|
34
37
|
const start = () => client.boot((next, update) => {
|
|
35
38
|
this.#root.instance.p = root.toProps(next, update);
|
|
36
39
|
this.#app.tick();
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import type { Signal } from "@angular/core";
|
|
2
|
-
import type { FormInit } from "@primate/core/client";
|
|
2
|
+
import type { ClientMethod, FormInit } from "@primate/core/client";
|
|
3
3
|
import type { Dict } from "@rcompat/type";
|
|
4
4
|
type Field<T> = {
|
|
5
5
|
name: string;
|
|
6
6
|
value: T;
|
|
7
|
-
errors: Signal<
|
|
7
|
+
errors: Signal<string[]>;
|
|
8
8
|
error: Signal<string | null>;
|
|
9
9
|
};
|
|
10
|
-
type FormView<Values extends Dict> = {
|
|
10
|
+
type FormView<Values extends Dict, Result = unknown> = {
|
|
11
11
|
id: string;
|
|
12
12
|
submitting: Signal<boolean>;
|
|
13
13
|
submitted: Signal<boolean>;
|
|
14
|
+
result: Signal<Result | null>;
|
|
14
15
|
submit: (event?: Event) => Promise<void>;
|
|
15
|
-
errors: Signal<
|
|
16
|
+
errors: Signal<string[]>;
|
|
16
17
|
field: <K extends keyof Values & string>(name: K) => Field<Values[K]>;
|
|
17
18
|
};
|
|
18
19
|
type Initial<Values extends Dict> = FormInit & {
|
|
19
20
|
initial?: Values;
|
|
20
21
|
};
|
|
22
|
+
declare function form<Values extends Dict, Path extends Dict, Result = unknown>(action: ClientMethod<Values, Path> & {
|
|
23
|
+
_result?: Result;
|
|
24
|
+
}, init?: Initial<Values> & {
|
|
25
|
+
path?: Path;
|
|
26
|
+
}): FormView<Values, Result>;
|
|
21
27
|
declare function form<Values extends Dict>(init: Initial<Values>): FormView<Values>;
|
|
22
28
|
declare function form(init?: FormInit): FormView<Dict>;
|
|
23
29
|
export default form;
|
|
@@ -8,36 +8,48 @@ function try_destroy_ref() {
|
|
|
8
8
|
return null;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
function form(
|
|
12
|
-
const
|
|
13
|
-
const
|
|
11
|
+
function form(action_or_init, maybe_init) {
|
|
12
|
+
const is_action = typeof action_or_init === "function";
|
|
13
|
+
const { initial, ...form_init } = (is_action ? (maybe_init ?? {}) : (action_or_init ?? {}));
|
|
14
|
+
const action = is_action ? action_or_init : undefined;
|
|
15
|
+
const contentType = is_action
|
|
16
|
+
? action_or_init.contentType
|
|
17
|
+
: undefined;
|
|
18
|
+
const controller = core.createForm({
|
|
19
|
+
...form_init,
|
|
20
|
+
...(action !== undefined && {
|
|
21
|
+
action: action,
|
|
22
|
+
contentType,
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
14
25
|
const values = (initial ?? {});
|
|
15
26
|
const snap = signal(controller.read());
|
|
16
|
-
const
|
|
17
|
-
try_destroy_ref()?.onDestroy(
|
|
27
|
+
const unsubscribe = controller.subscribe(next => snap.set(next));
|
|
28
|
+
try_destroy_ref()?.onDestroy(unsubscribe);
|
|
18
29
|
const submitting = computed(() => snap().submitting);
|
|
19
30
|
const submitted = computed(() => snap().submitted);
|
|
31
|
+
const result = computed(() => snap().result);
|
|
20
32
|
const errors = computed(() => snap().errors.form);
|
|
21
33
|
const cache = new Map();
|
|
22
34
|
function field(name) {
|
|
23
|
-
const
|
|
24
|
-
const cached = cache.get(key);
|
|
35
|
+
const cached = cache.get(name);
|
|
25
36
|
if (cached)
|
|
26
37
|
return cached;
|
|
27
|
-
const form_errors = computed(() => snap().errors.fields[
|
|
38
|
+
const form_errors = computed(() => snap().errors.fields[name] ?? []);
|
|
28
39
|
const view = {
|
|
29
|
-
name
|
|
40
|
+
name,
|
|
30
41
|
value: values[name],
|
|
31
42
|
errors: form_errors,
|
|
32
43
|
error: computed(() => form_errors()[0] ?? null),
|
|
33
44
|
};
|
|
34
|
-
cache.set(
|
|
45
|
+
cache.set(name, view);
|
|
35
46
|
return view;
|
|
36
47
|
}
|
|
37
48
|
return {
|
|
38
49
|
id: controller.id,
|
|
39
50
|
submitting,
|
|
40
51
|
submitted,
|
|
52
|
+
result,
|
|
41
53
|
submit: (event) => controller.submit(event),
|
|
42
54
|
errors,
|
|
43
55
|
field,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import root_selector from "#root-selector";
|
|
2
|
-
export default (depth
|
|
2
|
+
export default (depth) => {
|
|
3
3
|
const n = depth;
|
|
4
4
|
const layer = (i, child) => {
|
|
5
5
|
const slot = `slot_${i}`;
|
|
@@ -17,31 +17,17 @@ export default (depth, i18n_active) => {
|
|
|
17
17
|
let body = layer(n, null);
|
|
18
18
|
for (let i = n - 1; i >= 0; i--)
|
|
19
19
|
body = layer(i, body);
|
|
20
|
-
const i18n_imports = i18n_active
|
|
21
|
-
? `import t from "#i18n";
|
|
22
|
-
import sInternal from "primate/s/internal";`
|
|
23
|
-
: "";
|
|
24
|
-
const i18n_setup = i18n_active ?
|
|
25
|
-
`if (this.#p.request.context.i18n.locale) {
|
|
26
|
-
t[sInternal].init(this.#p.request.context.i18n.locale);
|
|
27
|
-
}
|
|
28
|
-
this.#off = t.subscribe(() => this.#cdr.markForCheck());
|
|
29
|
-
`
|
|
30
|
-
: "";
|
|
31
20
|
return `
|
|
32
21
|
import {
|
|
33
22
|
Component,
|
|
34
23
|
Input,
|
|
35
24
|
inject,
|
|
36
25
|
ChangeDetectorRef,
|
|
37
|
-
OnDestroy,
|
|
38
26
|
TemplateRef,
|
|
39
|
-
AfterViewInit,
|
|
40
27
|
} from "@angular/core";
|
|
41
28
|
import { CommonModule, NgComponentOutlet } from "@angular/common";
|
|
42
29
|
import INITIAL_PROPS from "@primate/angular/INITIAL_PROPS";
|
|
43
30
|
import { request } from "@primate/angular/app";
|
|
44
|
-
${i18n_imports}
|
|
45
31
|
|
|
46
32
|
type Dict = Record<string, any>;
|
|
47
33
|
type RootProps = {
|
|
@@ -57,10 +43,9 @@ type RootProps = {
|
|
|
57
43
|
imports: [CommonModule, NgComponentOutlet],
|
|
58
44
|
template: \`<ng-container *ngIf="P as P">${body}</ng-container>\`
|
|
59
45
|
})
|
|
60
|
-
export default class RootComponent
|
|
46
|
+
export default class RootComponent {
|
|
61
47
|
#p!: RootProps;
|
|
62
48
|
#cdr = inject(ChangeDetectorRef);
|
|
63
|
-
#off?: () => void;
|
|
64
49
|
|
|
65
50
|
constructor() {
|
|
66
51
|
try {
|
|
@@ -70,40 +55,30 @@ export default class RootComponent implements OnDestroy {
|
|
|
70
55
|
request.set(initial.request);
|
|
71
56
|
}
|
|
72
57
|
} catch {}
|
|
73
|
-
${i18n_setup}
|
|
74
58
|
}
|
|
75
59
|
|
|
76
60
|
@Input({ required: true })
|
|
77
61
|
set p(value: RootProps) {
|
|
78
62
|
this.#p = value;
|
|
79
63
|
request.set(value.request);
|
|
80
|
-
this.#cdr.markForCheck();
|
|
64
|
+
this.#cdr.markForCheck();
|
|
81
65
|
}
|
|
66
|
+
|
|
82
67
|
get p(): RootProps { return this.#p; }
|
|
83
68
|
get P(): RootProps { return this.#p; }
|
|
84
69
|
|
|
85
|
-
// is there a view at index i?
|
|
86
70
|
has(i: number) { return !!this.P?.views?.[i]; }
|
|
87
71
|
|
|
88
|
-
// view type for index i
|
|
89
72
|
comp(i: number) { return this.P?.views?.[i]; }
|
|
90
73
|
|
|
91
|
-
// per-layer inputs without slot
|
|
92
74
|
inputs(i: number) {
|
|
93
75
|
return (this.P?.props?.[i] ?? {}) as Dict;
|
|
94
76
|
}
|
|
95
77
|
|
|
96
|
-
// per-layer inputs + slot template (only used when has(i+1) is true)
|
|
97
78
|
slotInputs(i: number, slot: TemplateRef<unknown>) {
|
|
98
79
|
const base = (this.P?.props?.[i] ?? {}) as Dict;
|
|
99
80
|
return { ...base, slot };
|
|
100
81
|
}
|
|
101
|
-
|
|
102
|
-
${i18n_active ? `ngAfterViewInit() {
|
|
103
|
-
t[sInternal].restore();
|
|
104
|
-
}` : ""}
|
|
105
|
-
|
|
106
|
-
ngOnDestroy() { this.#off?.(); }
|
|
107
82
|
}
|
|
108
83
|
`;
|
|
109
84
|
};
|
package/lib/private/default.d.ts
CHANGED
package/lib/private/default.js
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import create_root from "#create-root";
|
|
2
2
|
import init from "#init";
|
|
3
|
+
import signal_inputs from "#signal-inputs";
|
|
3
4
|
import frontend from "@primate/core/frontend";
|
|
4
|
-
import
|
|
5
|
+
import ts from "typescript";
|
|
6
|
+
function compile(text, lib) {
|
|
7
|
+
const result = ts.transpileModule(signal_inputs(text), {
|
|
8
|
+
compilerOptions: {
|
|
9
|
+
module: ts.ModuleKind.ESNext,
|
|
10
|
+
target: ts.ScriptTarget.ES2020,
|
|
11
|
+
experimentalDecorators: true,
|
|
12
|
+
emitDecoratorMetadata: true,
|
|
13
|
+
lib,
|
|
14
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const style_match = text.match(/styles:\s*\[([\s\S]*?)\]/);
|
|
18
|
+
return {
|
|
19
|
+
js: result.outputText,
|
|
20
|
+
css: style_match === null ? "" : style_match[1]
|
|
21
|
+
.replace(/['"`]/g, "")
|
|
22
|
+
.replace(/\\n/g, "\n")
|
|
23
|
+
.trim(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
5
26
|
export default frontend({
|
|
6
27
|
...init,
|
|
7
28
|
root: {
|
|
@@ -11,41 +32,10 @@ export default frontend({
|
|
|
11
32
|
filter: /\.component\.(css)$/,
|
|
12
33
|
},
|
|
13
34
|
compile: {
|
|
14
|
-
client: (text) =>
|
|
15
|
-
const result = ts.transpileModule(text, {
|
|
16
|
-
compilerOptions: {
|
|
17
|
-
module: ts.ModuleKind.ESNext,
|
|
18
|
-
target: ts.ScriptTarget.ES2020,
|
|
19
|
-
experimentalDecorators: true,
|
|
20
|
-
emitDecoratorMetadata: true,
|
|
21
|
-
lib: ["es2020", "dom"],
|
|
22
|
-
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
// extract inline styles
|
|
26
|
-
const style_match = text.match(/styles:\s*\[([\s\S]*?)\]/);
|
|
27
|
-
return {
|
|
28
|
-
js: result.outputText,
|
|
29
|
-
css: style_match === null ? "" : style_match[1]
|
|
30
|
-
.replace(/['"`]/g, "")
|
|
31
|
-
.replace(/\\n/g, "\n")
|
|
32
|
-
.trim(),
|
|
33
|
-
};
|
|
34
|
-
},
|
|
35
|
+
client: (text) => compile(text, ["es2020", "dom"]),
|
|
35
36
|
server: (text) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
compilerOptions: {
|
|
39
|
-
module: ts.ModuleKind.ESNext,
|
|
40
|
-
target: ts.ScriptTarget.ES2020,
|
|
41
|
-
experimentalDecorators: true,
|
|
42
|
-
emitDecoratorMetadata: true,
|
|
43
|
-
lib: ["es2020"],
|
|
44
|
-
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
// hinder treeshaking
|
|
48
|
-
return `import "@angular/compiler";\n${result.outputText}`;
|
|
37
|
+
const result = compile(text, ["es2020"]);
|
|
38
|
+
return `import "@angular/compiler";\n${result.js}`;
|
|
49
39
|
},
|
|
50
40
|
},
|
|
51
41
|
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import request from "#request/store";
|
|
2
|
+
import { signal } from "@angular/core";
|
|
3
|
+
export default function i18n(source) {
|
|
4
|
+
const version = signal(0);
|
|
5
|
+
let restored = false;
|
|
6
|
+
let restored_request;
|
|
7
|
+
const current_request = () => request();
|
|
8
|
+
const restore_from_request = () => {
|
|
9
|
+
const next = current_request();
|
|
10
|
+
if (restored && next === restored_request)
|
|
11
|
+
return;
|
|
12
|
+
restored = true;
|
|
13
|
+
restored_request = next;
|
|
14
|
+
source.restore(next);
|
|
15
|
+
};
|
|
16
|
+
const depend = () => {
|
|
17
|
+
version();
|
|
18
|
+
restore_from_request();
|
|
19
|
+
};
|
|
20
|
+
const restore = (next = current_request()) => {
|
|
21
|
+
const before = source.locale.get();
|
|
22
|
+
restored = true;
|
|
23
|
+
restored_request = next;
|
|
24
|
+
source.restore(next);
|
|
25
|
+
if (source.locale.get() !== before)
|
|
26
|
+
version.update(value => value + 1);
|
|
27
|
+
};
|
|
28
|
+
const get = () => {
|
|
29
|
+
depend();
|
|
30
|
+
return source.locale.get();
|
|
31
|
+
};
|
|
32
|
+
const set = (locale) => {
|
|
33
|
+
const before = source.locale.get();
|
|
34
|
+
source.locale.set(locale);
|
|
35
|
+
if (source.locale.get() !== before)
|
|
36
|
+
version.update(value => value + 1);
|
|
37
|
+
};
|
|
38
|
+
const value = ((...args) => {
|
|
39
|
+
depend();
|
|
40
|
+
return source(...args);
|
|
41
|
+
});
|
|
42
|
+
value.defaultLocale = source.defaultLocale;
|
|
43
|
+
value.locales = source.locales;
|
|
44
|
+
value.catalogs = source.catalogs;
|
|
45
|
+
value.currency = source.currency;
|
|
46
|
+
value.locale = { get, set };
|
|
47
|
+
value.restore = restore;
|
|
48
|
+
restore();
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=i18n.js.map
|
package/lib/private/init.js
CHANGED
|
@@ -24,7 +24,7 @@ const module = {
|
|
|
24
24
|
document: `<${root}></${root}>`,
|
|
25
25
|
});
|
|
26
26
|
const headMatch = html.match(/<head>(.*?)<\/head>/s);
|
|
27
|
-
const bodyRegex = new RegExp(
|
|
27
|
+
const bodyRegex = new RegExp(`(<${root}(?:\\s[^>]*)?>[\\s\\S]*?<\\/${root}>)`);
|
|
28
28
|
const bodyMatch = html.match(bodyRegex);
|
|
29
29
|
return {
|
|
30
30
|
body: bodyMatch?.[1] || html,
|
package/lib/private/runtime.d.ts
CHANGED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
const factory = ts.factory;
|
|
3
|
+
const CORE_NS = "__primate_angular_core";
|
|
4
|
+
function TRUE() {
|
|
5
|
+
return factory.createTrue();
|
|
6
|
+
}
|
|
7
|
+
function UNDEFINED() {
|
|
8
|
+
return factory.createIdentifier("undefined");
|
|
9
|
+
}
|
|
10
|
+
function STRING_LITERAL(text) {
|
|
11
|
+
return factory.createStringLiteral(text);
|
|
12
|
+
}
|
|
13
|
+
function core(name) {
|
|
14
|
+
return factory.createPropertyAccessExpression(factory.createIdentifier(CORE_NS), name);
|
|
15
|
+
}
|
|
16
|
+
function is_angular_core_import(node) {
|
|
17
|
+
return ts.isImportDeclaration(node)
|
|
18
|
+
&& ts.isStringLiteral(node.moduleSpecifier)
|
|
19
|
+
&& node.moduleSpecifier.text === "@angular/core";
|
|
20
|
+
}
|
|
21
|
+
function import_name(node) {
|
|
22
|
+
return (node.propertyName ?? node.name).text;
|
|
23
|
+
}
|
|
24
|
+
function local_name(node) {
|
|
25
|
+
return node.name.text;
|
|
26
|
+
}
|
|
27
|
+
function collect_angular_core_refs(source) {
|
|
28
|
+
const input_names = new Set(["input"]);
|
|
29
|
+
const namespaces = new Set();
|
|
30
|
+
for (const statement of source.statements) {
|
|
31
|
+
if (!is_angular_core_import(statement))
|
|
32
|
+
continue;
|
|
33
|
+
const bindings = statement.importClause?.namedBindings;
|
|
34
|
+
if (bindings === undefined)
|
|
35
|
+
continue;
|
|
36
|
+
if (ts.isNamedImports(bindings)) {
|
|
37
|
+
for (const element of bindings.elements) {
|
|
38
|
+
if (import_name(element) === "input") {
|
|
39
|
+
input_names.add(local_name(element));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (ts.isNamespaceImport(bindings)) {
|
|
44
|
+
namespaces.add(bindings.name.text);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { input_names, namespaces };
|
|
48
|
+
}
|
|
49
|
+
function is_input_identifier_call(expr, refs) {
|
|
50
|
+
return ts.isCallExpression(expr)
|
|
51
|
+
&& ts.isIdentifier(expr.expression)
|
|
52
|
+
&& refs.input_names.has(expr.expression.text);
|
|
53
|
+
}
|
|
54
|
+
function is_input_namespace_call(expr, refs) {
|
|
55
|
+
return ts.isCallExpression(expr)
|
|
56
|
+
&& ts.isPropertyAccessExpression(expr.expression)
|
|
57
|
+
&& expr.expression.name.text === "input"
|
|
58
|
+
&& ts.isIdentifier(expr.expression.expression)
|
|
59
|
+
&& refs.namespaces.has(expr.expression.expression.text);
|
|
60
|
+
}
|
|
61
|
+
function is_input_call(expr, refs) {
|
|
62
|
+
return is_input_identifier_call(expr, refs)
|
|
63
|
+
|| is_input_namespace_call(expr, refs);
|
|
64
|
+
}
|
|
65
|
+
function is_required_input_identifier_call(expr, refs) {
|
|
66
|
+
return ts.isCallExpression(expr)
|
|
67
|
+
&& ts.isPropertyAccessExpression(expr.expression)
|
|
68
|
+
&& expr.expression.name.text === "required"
|
|
69
|
+
&& ts.isIdentifier(expr.expression.expression)
|
|
70
|
+
&& refs.input_names.has(expr.expression.expression.text);
|
|
71
|
+
}
|
|
72
|
+
function is_required_input_namespace_call(expr, refs) {
|
|
73
|
+
return ts.isCallExpression(expr)
|
|
74
|
+
&& ts.isPropertyAccessExpression(expr.expression)
|
|
75
|
+
&& expr.expression.name.text === "required"
|
|
76
|
+
&& ts.isPropertyAccessExpression(expr.expression.expression)
|
|
77
|
+
&& expr.expression.expression.name.text === "input"
|
|
78
|
+
&& ts.isIdentifier(expr.expression.expression.expression)
|
|
79
|
+
&& refs.namespaces.has(expr.expression.expression.expression.text);
|
|
80
|
+
}
|
|
81
|
+
function is_required_input_call(expr, refs) {
|
|
82
|
+
return is_required_input_identifier_call(expr, refs)
|
|
83
|
+
|| is_required_input_namespace_call(expr, refs);
|
|
84
|
+
}
|
|
85
|
+
function has_object_key(object, key) {
|
|
86
|
+
return object.properties.some(property => ts.isPropertyAssignment(property)
|
|
87
|
+
&& (ts.isIdentifier(property.name) && property.name.text === key
|
|
88
|
+
|| ts.isStringLiteral(property.name) && property.name.text === key));
|
|
89
|
+
}
|
|
90
|
+
function input_decorator_arg(public_name, options, required) {
|
|
91
|
+
if (options === undefined) {
|
|
92
|
+
if (!required)
|
|
93
|
+
return factory.createStringLiteral(public_name);
|
|
94
|
+
return factory.createObjectLiteralExpression([
|
|
95
|
+
factory.createPropertyAssignment("alias", STRING_LITERAL(public_name)),
|
|
96
|
+
factory.createPropertyAssignment("required", TRUE()),
|
|
97
|
+
], false);
|
|
98
|
+
}
|
|
99
|
+
if (ts.isObjectLiteralExpression(options)) {
|
|
100
|
+
const props = [...options.properties];
|
|
101
|
+
if (!has_object_key(options, "alias")) {
|
|
102
|
+
props.push(factory.createPropertyAssignment("alias", STRING_LITERAL(public_name)));
|
|
103
|
+
}
|
|
104
|
+
if (required && !has_object_key(options, "required")) {
|
|
105
|
+
props.push(factory.createPropertyAssignment("required", TRUE()));
|
|
106
|
+
}
|
|
107
|
+
return factory.createObjectLiteralExpression(props, false);
|
|
108
|
+
}
|
|
109
|
+
return factory.createObjectLiteralExpression([
|
|
110
|
+
factory.createSpreadAssignment(options),
|
|
111
|
+
factory.createPropertyAssignment("alias", STRING_LITERAL(public_name)),
|
|
112
|
+
...(required
|
|
113
|
+
? [factory.createPropertyAssignment("required", TRUE())]
|
|
114
|
+
: []),
|
|
115
|
+
], false);
|
|
116
|
+
}
|
|
117
|
+
function has_primate_core_namespace_import(source) {
|
|
118
|
+
return source.statements.some(statement => {
|
|
119
|
+
if (!is_angular_core_import(statement))
|
|
120
|
+
return false;
|
|
121
|
+
const bindings = statement.importClause?.namedBindings;
|
|
122
|
+
return bindings !== undefined
|
|
123
|
+
&& ts.isNamespaceImport(bindings)
|
|
124
|
+
&& bindings.name.text === CORE_NS;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function ensure_primate_core_namespace_import(source) {
|
|
128
|
+
if (has_primate_core_namespace_import(source))
|
|
129
|
+
return source;
|
|
130
|
+
const import_statement = factory.createImportDeclaration(undefined, factory.createImportClause(undefined, undefined, factory.createNamespaceImport(factory.createIdentifier(CORE_NS))), factory.createStringLiteral("@angular/core"));
|
|
131
|
+
return factory.updateSourceFile(source, [
|
|
132
|
+
import_statement,
|
|
133
|
+
...source.statements,
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
function transform_class_member(member, refs) {
|
|
137
|
+
if (!ts.isPropertyDeclaration(member)
|
|
138
|
+
|| member.initializer === undefined
|
|
139
|
+
|| !ts.isIdentifier(member.name)) {
|
|
140
|
+
return [member];
|
|
141
|
+
}
|
|
142
|
+
const name = member.name.text;
|
|
143
|
+
const initializer = member.initializer;
|
|
144
|
+
const regular = is_input_call(initializer, refs);
|
|
145
|
+
const required = is_required_input_call(initializer, refs);
|
|
146
|
+
if (!regular && !required)
|
|
147
|
+
return [member];
|
|
148
|
+
const args = initializer.arguments;
|
|
149
|
+
const default_value = regular
|
|
150
|
+
? args[0] ?? UNDEFINED()
|
|
151
|
+
: factory.createNonNullExpression(UNDEFINED());
|
|
152
|
+
const options = regular ? args[1] : args[0];
|
|
153
|
+
const signal_call = factory.createCallExpression(core("signal"), initializer.typeArguments, [default_value]);
|
|
154
|
+
const signal_property = factory.updatePropertyDeclaration(member, member.modifiers, member.name, member.questionToken, member.type, signal_call);
|
|
155
|
+
const decorator = factory.createDecorator(factory.createCallExpression(core("Input"), undefined, [input_decorator_arg(name, options, required)]));
|
|
156
|
+
const setter = factory.createSetAccessorDeclaration([decorator], factory.createIdentifier(`__primate_input_${name}`), [
|
|
157
|
+
factory.createParameterDeclaration(undefined, undefined, "value", undefined, factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
|
|
158
|
+
], factory.createBlock([
|
|
159
|
+
factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createThis(), name), "set"), undefined, [factory.createIdentifier("value")])),
|
|
160
|
+
], true));
|
|
161
|
+
return [signal_property, setter];
|
|
162
|
+
}
|
|
163
|
+
function transform_source(source) {
|
|
164
|
+
const refs = collect_angular_core_refs(source);
|
|
165
|
+
let found = false;
|
|
166
|
+
const statements = source.statements.map(statement => {
|
|
167
|
+
if (!ts.isClassDeclaration(statement))
|
|
168
|
+
return statement;
|
|
169
|
+
const members = statement.members.flatMap(member => {
|
|
170
|
+
const next = transform_class_member(member, refs);
|
|
171
|
+
if (next.length !== 1 || next[0] !== member) {
|
|
172
|
+
found = true;
|
|
173
|
+
}
|
|
174
|
+
return next;
|
|
175
|
+
});
|
|
176
|
+
return factory.updateClassDeclaration(statement, statement.modifiers, statement.name, statement.typeParameters, statement.heritageClauses, members);
|
|
177
|
+
});
|
|
178
|
+
if (!found)
|
|
179
|
+
return source;
|
|
180
|
+
return ensure_primate_core_namespace_import(factory.updateSourceFile(source, statements));
|
|
181
|
+
}
|
|
182
|
+
export default function signal_inputs(text) {
|
|
183
|
+
const source = ts.createSourceFile("component.ts", text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
184
|
+
const transformed = transform_source(source);
|
|
185
|
+
if (transformed === source)
|
|
186
|
+
return text;
|
|
187
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
188
|
+
return printer.printNode(ts.EmitHint.SourceFile, transformed, source);
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=signal-inputs.js.map
|
package/lib/public/browser.d.ts
CHANGED
package/lib/public/browser.js
CHANGED
package/lib/public/client.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primate/angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Angular for Primate",
|
|
5
5
|
"homepage": "https://primate.run/docs/frontend/angular",
|
|
6
6
|
"bugs": "https://github.com/primate-run/primate/issues",
|
|
@@ -18,21 +18,20 @@
|
|
|
18
18
|
],
|
|
19
19
|
"types": "lib/index.d.ts",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@angular/common": "^21.2.
|
|
22
|
-
"@angular/compiler": "^21.2.
|
|
23
|
-
"@angular/core": "^21.2.
|
|
24
|
-
"@angular/platform-browser": "^21.2.
|
|
25
|
-
"@angular/platform-server": "^21.2.
|
|
26
|
-
"@angular/ssr": "^21.2.
|
|
27
|
-
"typescript": "^
|
|
28
|
-
"@primate/core": "^0.
|
|
21
|
+
"@angular/common": "^21.2.15",
|
|
22
|
+
"@angular/compiler": "^21.2.15",
|
|
23
|
+
"@angular/core": "^21.2.15",
|
|
24
|
+
"@angular/platform-browser": "^21.2.15",
|
|
25
|
+
"@angular/platform-server": "^21.2.15",
|
|
26
|
+
"@angular/ssr": "^21.2.13",
|
|
27
|
+
"typescript": "^6.0.3",
|
|
28
|
+
"@primate/core": "^0.8.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@rcompat/type": "^0.
|
|
32
|
-
"@primate/test": "^0.0.0"
|
|
31
|
+
"@rcompat/type": "^0.17.0"
|
|
33
32
|
},
|
|
34
33
|
"peerDependencies": {
|
|
35
|
-
"primate": "^0.
|
|
34
|
+
"primate": "^0.39.0"
|
|
36
35
|
},
|
|
37
36
|
"imports": {
|
|
38
37
|
"#*": {
|
|
@@ -43,8 +42,12 @@
|
|
|
43
42
|
"exports": {
|
|
44
43
|
".": {
|
|
45
44
|
"types": "./lib/index.d.ts",
|
|
45
|
+
"browser": {
|
|
46
|
+
"@primate/source": "./src/public/browser.ts",
|
|
47
|
+
"default": "./lib/public/browser.js"
|
|
48
|
+
},
|
|
49
|
+
"@primate/runtime": "./src/public/runtime.ts",
|
|
46
50
|
"@primate/source": "./src/public/default.ts",
|
|
47
|
-
"browser": "./lib/public/browser.js",
|
|
48
51
|
"runtime": "./lib/public/runtime.js",
|
|
49
52
|
"default": "./lib/public/default.js"
|
|
50
53
|
},
|
|
@@ -54,9 +57,8 @@
|
|
|
54
57
|
}
|
|
55
58
|
},
|
|
56
59
|
"scripts": {
|
|
57
|
-
"build": "npm run clean &&
|
|
60
|
+
"build": "npm run clean && tsgo && cp src/types/{app,index}.d.ts lib",
|
|
58
61
|
"clean": "rm -rf ./lib",
|
|
59
|
-
"lint": "eslint ."
|
|
60
|
-
"test": "npx proby"
|
|
62
|
+
"lint": "eslint ."
|
|
61
63
|
}
|
|
62
64
|
}
|