@primate/marko 0.9.0 → 0.11.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.
@@ -0,0 +1,5 @@
1
+ type I18NModule = {
2
+ invalidate(fn: () => void): void;
3
+ };
4
+ export type { I18NModule as default };
5
+ //# sourceMappingURL=I18NModule.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=I18NModule.js.map
@@ -0,0 +1,5 @@
1
+ import type Payload from "#client/Payload";
2
+ import type { Data } from "@primate/core/client";
3
+ type MarkoData = Data<Payload>;
4
+ export type { MarkoData as default };
5
+ //# sourceMappingURL=Data.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Data.js.map
@@ -0,0 +1,7 @@
1
+ import type { Dict } from "@rcompat/type";
2
+ type Payload = {
3
+ views: string[];
4
+ props: Dict[];
5
+ };
6
+ export type { Payload as default };
7
+ //# sourceMappingURL=Payload.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Payload.js.map
@@ -0,0 +1,5 @@
1
+ import type Data from "#client/Data";
2
+ export default class MarkoApp {
3
+ static mount(_: string, data: Data): void;
4
+ }
5
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1,53 @@
1
+ import root from "#client/root";
2
+ import client from "@primate/core/client";
3
+ import RootView from "marko:root";
4
+ export default class MarkoApp {
5
+ static mount(_, data) {
6
+ const interactive = !data.ssr || data.csr;
7
+ if (!interactive)
8
+ return;
9
+ let instance;
10
+ let current = data;
11
+ let initial = data.ssr;
12
+ const key = (next) => `${next.views.join("\0")}\0${next.request.url}`;
13
+ const render = (next, remount = false) => {
14
+ current = next;
15
+ const props = root.toProps(next);
16
+ if (!remount && instance !== undefined) {
17
+ instance.update?.(props);
18
+ return;
19
+ }
20
+ instance?.destroy?.();
21
+ document.body.replaceChildren();
22
+ instance = RootView.mount(props, document.body);
23
+ };
24
+ const invalidate = () => render(current, true);
25
+ const mods = globalThis.__primate_i18n__ ?? [];
26
+ for (const mod of mods) {
27
+ mod.invalidate(invalidate);
28
+ }
29
+ if (!data.ssr) {
30
+ render(data, true);
31
+ }
32
+ if (data.csr) {
33
+ const start = () => client.boot((next, update) => {
34
+ if (initial && key(next) === key(current)) {
35
+ initial = false;
36
+ current = next;
37
+ update?.();
38
+ return;
39
+ }
40
+ initial = false;
41
+ render(next, instance === undefined);
42
+ update?.();
43
+ });
44
+ if (document.readyState === "loading") {
45
+ window.addEventListener("DOMContentLoaded", start, { once: true });
46
+ }
47
+ else {
48
+ start();
49
+ }
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1,15 @@
1
+ import type Data from "#client/Data";
2
+ import type { Dict } from "@rcompat/type";
3
+ export type RootProps = {
4
+ views: unknown[];
5
+ props: Dict[];
6
+ request: Data["request"] & {
7
+ url: URL;
8
+ };
9
+ };
10
+ declare function toProps(data: Data): RootProps;
11
+ declare const root: {
12
+ toProps: typeof toProps;
13
+ };
14
+ export default root;
15
+ //# sourceMappingURL=root.d.ts.map
@@ -0,0 +1,16 @@
1
+ import * as views from "marko:views";
2
+ function toProps(data) {
3
+ return {
4
+ views: data.views.map(name => views[name]),
5
+ props: data.props,
6
+ request: {
7
+ ...data.request,
8
+ url: new URL(data.request.url),
9
+ },
10
+ };
11
+ }
12
+ const root = {
13
+ toProps,
14
+ };
15
+ export default root;
16
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1,3 @@
1
+ export default _default;
2
+ declare function _default(depth: number): string;
3
+ //# sourceMappingURL=create-root.d.ts.map
@@ -0,0 +1,20 @@
1
+ export default (depth) => {
2
+ const view = (i) => `<\${input.views[${i}]} ...input.props[${i}] />`;
3
+ const layout = (i, child) => [
4
+ `<if=input.views[${i + 1}]>`,
5
+ `<\${input.views[${i}]} ...input.props[${i}]>`,
6
+ child,
7
+ "</>",
8
+ "</if>",
9
+ "<else>",
10
+ view(i),
11
+ "</else>",
12
+ ].join("\n");
13
+ const body = Array.from({ length: depth }, (_, i) => i)
14
+ .reduceRight((child, i) => layout(i, child), view(depth));
15
+ return `import { request } from "@primate/marko/app";
16
+ <const/{ context, path, ...public_request }=input.request/>
17
+ <const/_request=request.set(public_request)/>
18
+ ${body}`;
19
+ };
20
+ //# sourceMappingURL=create-root.js.map
@@ -1,11 +1,93 @@
1
+ import create_root from "#create-root";
1
2
  import init from "#init";
2
3
  import { compileSync } from "@marko/compiler";
3
4
  import frontend from "@primate/core/frontend";
5
+ const styles = new Map();
6
+ let style_id = 0;
7
+ let root_dir = "";
8
+ let root_file = "";
9
+ let root_dom = "";
10
+ function resolveVirtualDependency(_from, dependency) {
11
+ if (!dependency.virtualPath.endsWith(".css")) {
12
+ return dependency.virtualPath;
13
+ }
14
+ const path = `marko:style:${style_id++}`;
15
+ styles.set(path, {
16
+ code: dependency.code,
17
+ module: dependency.virtualPath.endsWith(".module.css"),
18
+ });
19
+ return path;
20
+ }
21
+ function compile(text, file, output, root = false) {
22
+ return compileSync(text, root || file.path.startsWith("root:")
23
+ ? root_file
24
+ : file.path, {
25
+ output,
26
+ resolveVirtualDependency,
27
+ });
28
+ }
29
+ const plugin = {
30
+ name: "marko-primate",
31
+ setup(build) {
32
+ build.onResolve({ filter: /^marko:style:/ }, args => ({
33
+ path: args.path,
34
+ namespace: "marko-style",
35
+ }));
36
+ build.onLoad({ filter: /.*/, namespace: "marko-style" }, args => {
37
+ const style = styles.get(args.path);
38
+ return style === undefined
39
+ ? null
40
+ : {
41
+ contents: style.code,
42
+ loader: style.module ? "local-css" : "css",
43
+ };
44
+ });
45
+ build.onResolve({ filter: /^\.\/root\.marko$/ }, args => {
46
+ if (args.namespace !== "marko" || args.importer !== "marko:root") {
47
+ return null;
48
+ }
49
+ return {
50
+ path: root_file,
51
+ namespace: "file",
52
+ };
53
+ });
54
+ build.onLoad({ filter: /.*/, namespace: "file" }, args => {
55
+ if (args.path !== root_file)
56
+ return null;
57
+ return {
58
+ contents: root_dom,
59
+ loader: "js",
60
+ resolveDir: root_dir,
61
+ };
62
+ });
63
+ },
64
+ };
4
65
  export default frontend({
5
66
  ...init,
67
+ root: {
68
+ create: create_root,
69
+ },
6
70
  compile: {
7
- server: (text, file) => compileSync(text, file.path).code,
8
- client: (text) => ({ js: text }),
71
+ server: (text, file) => compile(text, file, "html").code,
72
+ client: (text, file, root, hydrate) => {
73
+ if (root && hydrate) {
74
+ root_dom = compile(text, file, "dom", true).code;
75
+ return {
76
+ js: `${compile(text, file, "hydrate", true).code}
77
+ export { default } from "./root.marko";`,
78
+ };
79
+ }
80
+ return {
81
+ js: compile(text, file, "dom", root).code,
82
+ };
83
+ },
84
+ },
85
+ onBuild(app, options) {
86
+ root_dir = app.root.path;
87
+ root_file = app.root.join("root.marko").path;
88
+ app.plugin("server", plugin);
89
+ app.plugin("client", plugin);
90
+ return init.onBuild?.(app, options);
9
91
  },
10
92
  });
11
93
  //# sourceMappingURL=default.js.map
@@ -0,0 +1,3 @@
1
+ import type { BridgedTranslator, HeadlessTranslator } from "@primate/core/i18n";
2
+ export default function i18n<const T extends HeadlessTranslator>(source: T): BridgedTranslator<T>;
3
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1,38 @@
1
+ import request from "#request/store";
2
+ export default function i18n(source) {
3
+ let invalidate = () => undefined;
4
+ let forced;
5
+ const get = () => source.locale.get();
6
+ const set = (locale) => {
7
+ forced = locale;
8
+ source.locale.set(locale);
9
+ invalidate();
10
+ };
11
+ const restore = (next) => {
12
+ const before = source.locale.get();
13
+ source.restore(next);
14
+ if (forced !== undefined) {
15
+ source.locale.set(forced);
16
+ return;
17
+ }
18
+ if (source.locale.get() !== before)
19
+ invalidate();
20
+ };
21
+ const value = ((...args) => {
22
+ return source(...args);
23
+ });
24
+ value.defaultLocale = source.defaultLocale;
25
+ value.locales = source.locales;
26
+ value.catalogs = source.catalogs;
27
+ value.currency = source.currency;
28
+ value.locale = { get, set };
29
+ value.restore = restore;
30
+ value.invalidate = (fn) => { invalidate = fn; };
31
+ source.restore();
32
+ request.subscribe(next => {
33
+ restore(next);
34
+ });
35
+ (globalThis.__primate_i18n__ ??= []).push(value);
36
+ return value;
37
+ }
38
+ //# sourceMappingURL=i18n.js.map
@@ -1,5 +1,5 @@
1
1
  import type { Init } from "@primate/core/frontend";
2
- import type { Renderable } from "marko/src/runtime/html/Template.js";
3
- declare const module: Init<Renderable>;
2
+ import type { Template } from "marko/src/runtime/html/index.js";
3
+ declare const module: Init<Template>;
4
4
  export default module;
5
5
  //# sourceMappingURL=init.d.ts.map
@@ -1,10 +1,11 @@
1
1
  const module = {
2
2
  name: "marko",
3
3
  extensions: [".marko"],
4
- layouts: false,
5
- client: false,
6
- render(component, props) {
7
- return { body: component.renderToString(props) };
4
+ conditions: ["marko"],
5
+ layouts: true,
6
+ client: true,
7
+ async render(component, props) {
8
+ return { body: (await component.render(props)).toString() };
8
9
  },
9
10
  };
10
11
  export default module;
@@ -0,0 +1,14 @@
1
+ type Input = {
2
+ url: string | URL;
3
+ [key: string]: unknown;
4
+ };
5
+ type Subscriber = (value: MarkoRequest) => void;
6
+ type MarkoRequest = {
7
+ url: URL;
8
+ set(value: Input): void;
9
+ subscribe(subscriber: Subscriber): () => void;
10
+ [key: string]: unknown;
11
+ };
12
+ declare const request: MarkoRequest;
13
+ export default request;
14
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,20 @@
1
+ const subscribers = new Set();
2
+ const request = {
3
+ set(value) {
4
+ Object.assign(request, {
5
+ ...value,
6
+ url: value.url instanceof URL ? value.url : new URL(value.url),
7
+ });
8
+ for (const subscriber of subscribers) {
9
+ subscriber(request);
10
+ }
11
+ },
12
+ subscribe(subscriber) {
13
+ subscribers.add(subscriber);
14
+ return () => {
15
+ subscribers.delete(subscriber);
16
+ };
17
+ },
18
+ };
19
+ export default request;
20
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#tags/Field";
2
+ //# sourceMappingURL=Field.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#tags/Field";
2
+ //# sourceMappingURL=Field.js.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#tags/Form";
2
+ //# sourceMappingURL=Form.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#tags/Form";
2
+ //# sourceMappingURL=Form.js.map
@@ -0,0 +1,2 @@
1
+ export { default as request } from "#request/store";
2
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default as request } from "#request/store";
2
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1,6 @@
1
+ export default function marko(): {
2
+ name: string;
3
+ setup(): void;
4
+ };
5
+ export { default as app } from "#client/app";
6
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1,8 @@
1
+ export default function marko() {
2
+ return {
3
+ name: "marko",
4
+ setup() { },
5
+ };
6
+ }
7
+ export { default as app } from "#client/app";
8
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#i18n";
2
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#i18n";
2
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1,3 @@
1
+ export { default as Field } from "#tags/Field";
2
+ export { default as Form } from "#tags/Form";
3
+ //# sourceMappingURL=tags.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { default as Field } from "#tags/Field";
2
+ export { default as Form } from "#tags/Form";
3
+ //# sourceMappingURL=tags.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primate/marko",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Marko for Primate",
5
5
  "homepage": "https://primate.run/docs/frontend/marko",
6
6
  "bugs": "https://github.com/primate-run/primate/issues",
@@ -14,18 +14,24 @@
14
14
  "files": [
15
15
  "/lib/**/*.js",
16
16
  "/lib/**/*.d.ts",
17
+ "/src/**/*.marko",
17
18
  "!/**/*.spec.*"
18
19
  ],
19
20
  "dependencies": {
20
- "@marko/compiler": "^5.39.63",
21
- "@rcompat/fs": "^0.33.2",
22
- "marko": "^5.38.37",
23
- "@primate/core": "^0.7.0"
21
+ "@marko/compiler": "^5.40.2",
22
+ "@rcompat/fs": "^0.35.1",
23
+ "marko": "^6.3.3",
24
+ "@primate/core": "^0.9.0"
24
25
  },
25
26
  "devDependencies": {
26
- "@types/marko": "^4.6.9"
27
+ "@rcompat/type": "^0.18.0",
28
+ "@types/marko": "^4.6.9",
29
+ "esbuild": "^0.28.1"
27
30
  },
28
31
  "imports": {
32
+ "#tags/*": {
33
+ "marko": "./src/private/tags/*.marko"
34
+ },
29
35
  "#*": {
30
36
  "@primate/source": "./src/private/*.ts",
31
37
  "default": "./lib/private/*.js"
@@ -34,8 +40,13 @@
34
40
  "exports": {
35
41
  ".": {
36
42
  "@primate/source": "./src/public/default.ts",
43
+ "browser": "./lib/public/browser.js",
37
44
  "runtime": "./lib/public/runtime.js",
38
45
  "default": "./lib/public/default.js"
46
+ },
47
+ "./*": {
48
+ "@primate/source": "./src/public/*.ts",
49
+ "default": "./lib/public/*.js"
39
50
  }
40
51
  },
41
52
  "scripts": {
@@ -0,0 +1,44 @@
1
+ import client from "@primate/core/client";
2
+
3
+ export interface Input {
4
+ value: unknown;
5
+ method: "delete" | "patch" | "post" | "put";
6
+ url: string;
7
+ headers?: Record<string, string>;
8
+ map?: (value: unknown) => unknown;
9
+ path?: string;
10
+ }
11
+
12
+ <let/value=input.value/>
13
+ <let/error=null/>
14
+ <let/loading=false/>
15
+
16
+ <const/update=async (updater) => {
17
+ const previous = value;
18
+ const next = updater(value);
19
+ error = null;
20
+ loading = true;
21
+ value = next;
22
+ try {
23
+ await client.validateField({
24
+ initial: input.value,
25
+ method: input.method.toUpperCase(),
26
+ url: input.url,
27
+ headers: input.headers,
28
+ map: input.map,
29
+ path: input.path,
30
+ }, next);
31
+ loading = false;
32
+ } catch (e) {
33
+ error = e;
34
+ loading = false;
35
+ value = previous;
36
+ }
37
+ }/>
38
+
39
+ <return={
40
+ value,
41
+ error,
42
+ loading,
43
+ update
44
+ }/>
@@ -0,0 +1,110 @@
1
+ import client from "@primate/core/client";
2
+
3
+ <const/error_view=errors => {
4
+ const view = [...errors.form];
5
+
6
+ Object.defineProperty(view, "fields", {
7
+ value: errors.fields,
8
+ enumerable: false,
9
+ });
10
+
11
+ return view;
12
+ }/>
13
+
14
+ <let/id=`form-${crypto.randomUUID()}`/>
15
+ <let/submitting=false/>
16
+ <let/submitted=false/>
17
+ <let/result/>
18
+ <let/errors=error_view({ form: [], fields: {} })/>
19
+ <let/values=input.initial ?? {}/>
20
+ <let/controller/>
21
+ <let/action/>
22
+ <let/contentType/>
23
+ <let/method/>
24
+
25
+ <const/action_meta=typeof document === "undefined" ? {
26
+ method: input.action === undefined ? undefined : "POST",
27
+ contentType: input.action?.contentType,
28
+ } : {
29
+ action: input.action,
30
+ method: input.action === undefined ? undefined : "POST",
31
+ contentType: input.action?.contentType,
32
+ }/>
33
+
34
+ <const/set_action=meta => {
35
+ if ("action" in meta) {
36
+ action = meta.action;
37
+ }
38
+
39
+ method = meta.method;
40
+ contentType = meta.contentType;
41
+ }/>
42
+
43
+ <const/_action=set_action(action_meta)/>
44
+
45
+ <const/create_controller=() => {
46
+ if (controller !== undefined) return controller;
47
+
48
+ const fallback_action = action === undefined && method !== undefined
49
+ ? ({ body }) => fetch(globalThis.location.pathname, {
50
+ method,
51
+ headers: contentType === undefined || contentType === "multipart/form-data"
52
+ ? {}
53
+ : { "Content-Type": contentType },
54
+ body: contentType === "application/json"
55
+ ? JSON.stringify(body)
56
+ : contentType === "application/x-www-form-urlencoded"
57
+ ? new URLSearchParams(body)
58
+ : body,
59
+ })
60
+ : undefined;
61
+
62
+ controller = client.createForm({
63
+ id,
64
+ action: action ?? fallback_action,
65
+ contentType,
66
+ method,
67
+ path: input.path,
68
+ });
69
+
70
+ return controller;
71
+ }/>
72
+
73
+ <const/submit=async (event, element) => {
74
+ event?.preventDefault?.();
75
+
76
+ const controller = create_controller();
77
+
78
+ submitting = true;
79
+
80
+ await controller.submit({
81
+ preventDefault() {
82
+ event?.preventDefault?.();
83
+ },
84
+ stopPropagation() {
85
+ event?.stopPropagation?.();
86
+ },
87
+ currentTarget: element ?? document.getElementById(id),
88
+ });
89
+
90
+ const snapshot = controller.read();
91
+
92
+ id = snapshot.id;
93
+ submitting = snapshot.submitting;
94
+ submitted = snapshot.submitted;
95
+ result = snapshot.result;
96
+ errors = error_view(snapshot.errors);
97
+ }/>
98
+
99
+ <const/field=name => {
100
+ const field_errors = errors.fields[name] ?? [];
101
+
102
+ return {
103
+ name,
104
+ value: values[name],
105
+ error: field_errors[0] ?? null,
106
+ errors: field_errors,
107
+ };
108
+ }/>
109
+
110
+ <return={ id, submitting, submitted, result, errors, values, field, submit }/>