create-outsystems-astro 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.
- package/bin/cli.js +42 -1
- package/integrations/.prettierignore +15 -0
- package/integrations/.yarn/releases/yarn-4.15.0.cjs +940 -0
- package/integrations/.yarnrc.yml +6 -0
- package/integrations/bun.lock +1250 -0
- package/integrations/bunfig.toml +3 -0
- package/integrations/deno.json +4 -0
- package/integrations/deno.lock +3943 -0
- package/integrations/eslint.config.mjs +61 -0
- package/integrations/html/client.ts +30 -0
- package/integrations/html/index.ts +57 -0
- package/integrations/html/server.ts +54 -0
- package/integrations/package-lock.json +8926 -0
- package/integrations/package.json +42 -0
- package/integrations/pnpm-lock.yaml +5562 -0
- package/integrations/pnpm-workspace.yaml +4 -0
- package/integrations/tsconfig.json +16 -0
- package/integrations/twig/client.ts +34 -0
- package/integrations/twig/index.ts +185 -0
- package/integrations/twig/server.ts +54 -0
- package/integrations/yarn.lock +6375 -0
- package/package.json +3 -1
- package/template/.github/workflows/deno-test.yml +1 -1
- package/template/.github/workflows/npm-test.yml +1 -1
- package/template/.github/workflows/pnpm-test.yml +2 -2
- package/template/.github/workflows/yarn-test.yml +26 -13
- package/template/.gitignore +4 -0
- package/template/.prettierignore +1 -0
- package/template/.yarn/releases/yarn-4.15.0.cjs +940 -0
- package/template/.yarnrc.yml +8 -0
- package/template/AGENTS.md +93 -1
- package/template/README.md +15 -0
- package/template/astro.config.mjs +8 -0
- package/template/bun.lock +281 -367
- package/template/bunfig.toml +3 -0
- package/template/deno.json +3 -11
- package/template/deno.lock +703 -676
- package/template/eslint.config.mjs +1 -0
- package/template/package-lock.json +705 -787
- package/template/package.json +38 -41
- package/template/pnpm-lock.yaml +1126 -1114
- package/template/pnpm-workspace.yaml +5 -0
- package/template/src/env.d.ts +6 -0
- package/template/src/framework/html/Demo.ts +105 -0
- package/template/src/framework/html/Store.ts +47 -0
- package/template/src/framework/twig/Demo.twig +100 -0
- package/template/src/framework/twig/Store.twig +45 -0
- package/template/src/images/html.png +0 -0
- package/template/src/images/twig.png +0 -0
- package/template/src/pages/html/html-demo.astro +61 -0
- package/template/src/pages/multi/store.astro +13 -1
- package/template/src/pages/twig/twig-demo.astro +65 -0
- package/template/src/stores/framework.ts +2 -0
- package/template/test/e2e/html/html-demo.spec.ts +36 -0
- package/template/test/e2e/twig/twig-demo.spec.ts +36 -0
- package/template/test/integration/html/Demo.test.ts +83 -0
- package/template/test/integration/twig/Demo.test.ts +84 -0
- package/template/tsconfig.json +1 -1
- package/template/vitest.config.ts +18 -0
- package/template/yarn.lock +14777 -10350
- /package/template/patches/{@analogjs+astro-angular+2.5.1.patch → @analogjs+astro-angular+2.5.2.patch} +0 -0
- /package/template/patches/{@angular+build+21.2.11.patch → @angular+build+21.2.12.patch} +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "ESNext",
|
|
4
|
+
"moduleResolution": "Bundler",
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": ".",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"skipLibCheck": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["html/**/*.ts", "twig/**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Twig from "twig";
|
|
2
|
+
|
|
3
|
+
const client_default =
|
|
4
|
+
(element: HTMLElement) =>
|
|
5
|
+
(
|
|
6
|
+
Component: unknown,
|
|
7
|
+
props: Record<string, unknown>,
|
|
8
|
+
{ client }: { client: string },
|
|
9
|
+
) => {
|
|
10
|
+
if (client !== "only" && !element.hasAttribute("ssr")) return;
|
|
11
|
+
|
|
12
|
+
let template: string;
|
|
13
|
+
if (typeof Component === "string") {
|
|
14
|
+
template = Component;
|
|
15
|
+
} else if (typeof Component === "function") {
|
|
16
|
+
template = (Component as (p: Record<string, unknown>) => string)({
|
|
17
|
+
...props,
|
|
18
|
+
});
|
|
19
|
+
} else {
|
|
20
|
+
template = "";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const html = Twig.twig({ data: template }).render({ ...props });
|
|
24
|
+
|
|
25
|
+
element.innerHTML = html;
|
|
26
|
+
|
|
27
|
+
element.querySelectorAll("script").forEach((oldScript) => {
|
|
28
|
+
const newScript = document.createElement("script");
|
|
29
|
+
newScript.textContent = oldScript.textContent;
|
|
30
|
+
oldScript.parentNode?.replaceChild(newScript, oldScript);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default client_default;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import type { AstroIntegration, AstroRenderer } from "astro";
|
|
6
|
+
|
|
7
|
+
const VIRTUAL_SERVER_ID = "virtual:islands/twig/server-with-filter";
|
|
8
|
+
const RESOLVED_VIRTUAL_SERVER_ID = `\0${VIRTUAL_SERVER_ID}`;
|
|
9
|
+
|
|
10
|
+
function getRenderer(filtered: boolean): AstroRenderer {
|
|
11
|
+
return {
|
|
12
|
+
clientEntrypoint: "islands-integrations/twig/client",
|
|
13
|
+
name: "islands/twig",
|
|
14
|
+
serverEntrypoint: filtered
|
|
15
|
+
? VIRTUAL_SERVER_ID
|
|
16
|
+
: "islands-integrations/twig/server",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getContainerRenderer = (): AstroRenderer => getRenderer(false);
|
|
21
|
+
|
|
22
|
+
export interface Options {
|
|
23
|
+
exclude?: string[];
|
|
24
|
+
include?: string[];
|
|
25
|
+
namespaces?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Namespaces = Record<string, string>;
|
|
29
|
+
|
|
30
|
+
function normalizeNamespaces(
|
|
31
|
+
namespaces: Record<string, string> | undefined,
|
|
32
|
+
root: string,
|
|
33
|
+
): Namespaces {
|
|
34
|
+
const normalized: Namespaces = {};
|
|
35
|
+
for (const [name, dir] of Object.entries(namespaces ?? {})) {
|
|
36
|
+
normalized[name.replace(/^@/, "")] = path.resolve(root, dir);
|
|
37
|
+
}
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const NAMESPACE_PATH = /^@([^/]+)\/(.*)$/;
|
|
42
|
+
|
|
43
|
+
function resolveIncludePath(
|
|
44
|
+
includePath: string,
|
|
45
|
+
dir: string,
|
|
46
|
+
namespaces: Namespaces,
|
|
47
|
+
): string {
|
|
48
|
+
const match = NAMESPACE_PATH.exec(includePath);
|
|
49
|
+
if (!match) return path.resolve(dir, includePath);
|
|
50
|
+
|
|
51
|
+
const [, name, rest] = match;
|
|
52
|
+
const base = namespaces[name];
|
|
53
|
+
if (!base) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Twig namespace "@${name}" is not configured. Add it to the \`namespaces\` option of the twig() integration.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return path.resolve(base, rest);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function twigFilterPlugin(include?: string[], exclude?: string[]) {
|
|
62
|
+
return {
|
|
63
|
+
load(id: string) {
|
|
64
|
+
if (id !== RESOLVED_VIRTUAL_SERVER_ID) return;
|
|
65
|
+
return [
|
|
66
|
+
`import { createRenderer } from "islands-integrations/twig/server";`,
|
|
67
|
+
`export default createRenderer(${JSON.stringify(include)}, ${JSON.stringify(exclude)});`,
|
|
68
|
+
].join("\n");
|
|
69
|
+
},
|
|
70
|
+
name: "islands/twig/filter",
|
|
71
|
+
resolveId(id: string) {
|
|
72
|
+
if (id === VIRTUAL_SERVER_ID) return RESOLVED_VIRTUAL_SERVER_ID;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const INCLUDE_TAG = /\{%-?\s*include\s+(['"])([^'"]+)\1([^%]*?)-?%\}/g;
|
|
78
|
+
|
|
79
|
+
interface IncludeModifiers {
|
|
80
|
+
ignoreMissing: boolean;
|
|
81
|
+
only: boolean;
|
|
82
|
+
withExpr?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function applyContextScope(
|
|
86
|
+
body: string,
|
|
87
|
+
{ only, withExpr }: IncludeModifiers,
|
|
88
|
+
): string {
|
|
89
|
+
if (!withExpr && !only) return body;
|
|
90
|
+
const vars = withExpr ?? "{}";
|
|
91
|
+
const onlyFlag = only ? " only" : "";
|
|
92
|
+
return `{% with ${vars}${onlyFlag} %}${body}{% endwith %}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function inlineIncludes(
|
|
96
|
+
filepath: string,
|
|
97
|
+
ancestors: string[],
|
|
98
|
+
namespaces: Namespaces,
|
|
99
|
+
onInclude?: (target: string) => void,
|
|
100
|
+
): string {
|
|
101
|
+
const resolved = path.resolve(filepath);
|
|
102
|
+
if (ancestors.includes(resolved)) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Twig include cycle detected: ${[...ancestors, resolved].join(" -> ")}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const source = fs.readFileSync(resolved, "utf-8");
|
|
109
|
+
const dir = path.dirname(resolved);
|
|
110
|
+
const trail = [...ancestors, resolved];
|
|
111
|
+
|
|
112
|
+
return source.replace(
|
|
113
|
+
INCLUDE_TAG,
|
|
114
|
+
(_match, _quote, includePath, modifiers) => {
|
|
115
|
+
const parsed = parseModifiers(modifiers);
|
|
116
|
+
const target = resolveIncludePath(includePath, dir, namespaces);
|
|
117
|
+
if (!fs.existsSync(target)) {
|
|
118
|
+
if (parsed.ignoreMissing) return "";
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Twig include "${includePath}" not found (referenced from ${resolved}).`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
onInclude?.(target);
|
|
124
|
+
const body = inlineIncludes(target, trail, namespaces, onInclude);
|
|
125
|
+
return applyContextScope(body, parsed);
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseModifiers(modifiers: string): IncludeModifiers {
|
|
131
|
+
const ignoreMissing = /\bignore\s+missing\b/.test(modifiers);
|
|
132
|
+
let rest = modifiers.replace(/\bignore\s+missing\b/, " ");
|
|
133
|
+
|
|
134
|
+
const only = /\bonly\s*$/.test(rest);
|
|
135
|
+
rest = rest.replace(/\bonly\s*$/, " ");
|
|
136
|
+
|
|
137
|
+
const withMatch = /\bwith\b([\s\S]*)$/.exec(rest);
|
|
138
|
+
const withExpr = withMatch ? withMatch[1].trim() : undefined;
|
|
139
|
+
|
|
140
|
+
return { ignoreMissing, only, withExpr: withExpr || undefined };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function twigLoaderPlugin(namespaces: Namespaces) {
|
|
144
|
+
return {
|
|
145
|
+
enforce: "pre" as const,
|
|
146
|
+
load(this: { addWatchFile?: (id: string) => void }, id: string) {
|
|
147
|
+
const filepath = id.split("?")[0];
|
|
148
|
+
if (!filepath.endsWith(".twig")) return;
|
|
149
|
+
const source = inlineIncludes(filepath, [], namespaces, (target) =>
|
|
150
|
+
this.addWatchFile?.(target),
|
|
151
|
+
);
|
|
152
|
+
return {
|
|
153
|
+
code: `export default ${JSON.stringify(source)};`,
|
|
154
|
+
map: null,
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
name: "islands/twig/loader",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default function (options: Options = {}): AstroIntegration {
|
|
162
|
+
const { exclude, include, namespaces } = options;
|
|
163
|
+
const filtered = !!(include?.length || exclude?.length);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
hooks: {
|
|
167
|
+
"astro:config:setup": ({ addRenderer, config, updateConfig }) => {
|
|
168
|
+
addRenderer(getRenderer(filtered));
|
|
169
|
+
const resolvedNamespaces = normalizeNamespaces(
|
|
170
|
+
namespaces,
|
|
171
|
+
fileURLToPath(config.root),
|
|
172
|
+
);
|
|
173
|
+
const loader = twigLoaderPlugin(resolvedNamespaces);
|
|
174
|
+
updateConfig({
|
|
175
|
+
vite: {
|
|
176
|
+
plugins: filtered
|
|
177
|
+
? [loader, twigFilterPlugin(include, exclude)]
|
|
178
|
+
: [loader],
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
name: "islands/twig",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AstroComponentMetadata,
|
|
3
|
+
NamedSSRLoadedRendererValue,
|
|
4
|
+
} from "astro";
|
|
5
|
+
|
|
6
|
+
export function createRenderer(
|
|
7
|
+
include?: string[],
|
|
8
|
+
exclude?: string[],
|
|
9
|
+
): NamedSSRLoadedRendererValue {
|
|
10
|
+
return {
|
|
11
|
+
check: async (
|
|
12
|
+
Component: unknown,
|
|
13
|
+
_props: unknown,
|
|
14
|
+
_slots: unknown,
|
|
15
|
+
metadata?: AstroComponentMetadata,
|
|
16
|
+
) => {
|
|
17
|
+
const url = metadata?.componentUrl;
|
|
18
|
+
if (url) {
|
|
19
|
+
if (include && !matchesPatterns(url, include)) return false;
|
|
20
|
+
if (exclude && matchesPatterns(url, exclude)) return false;
|
|
21
|
+
}
|
|
22
|
+
return checkComponent(Component);
|
|
23
|
+
},
|
|
24
|
+
name: "islands/twig",
|
|
25
|
+
renderToStaticMarkup,
|
|
26
|
+
supportsAstroStaticSlot: false,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function checkComponent(Component: unknown): Promise<boolean> {
|
|
31
|
+
if (typeof Component === "string") return true;
|
|
32
|
+
if (typeof Component === "function") {
|
|
33
|
+
try {
|
|
34
|
+
const result = (Component as (p: Record<string, unknown>) => unknown)({});
|
|
35
|
+
return typeof result === "string";
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function matchesPatterns(url: string, patterns: string[]): boolean {
|
|
44
|
+
return patterns.some((pattern) => {
|
|
45
|
+
const prefix = pattern.replace(/\/?\*+$/, "");
|
|
46
|
+
return url.includes(prefix);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function renderToStaticMarkup(): Promise<{ html: string }> {
|
|
51
|
+
return { html: "" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default createRenderer();
|