actionview-svelte-handler 0.2.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ts/builder.ts ADDED
@@ -0,0 +1,174 @@
1
+ import { readable, Stores } from 'svelte/store'
2
+ import { importFromStringSync } from 'module-from-string'
3
+ import * as esbuild from 'esbuild'
4
+ import sveltePlugin from 'esbuild-svelte'
5
+ import { sveltePreprocess } from 'svelte-preprocess'
6
+ import type { Warning } from 'svelte/types/compiler/interfaces'
7
+ import * as recast from 'recast'
8
+
9
+ export interface BuildSuccess {
10
+ client: string
11
+ server: {
12
+ html: string
13
+ css: string
14
+ } | null
15
+ compiled: {
16
+ client: string
17
+ server: string | undefined
18
+ }
19
+ }
20
+
21
+ export interface BuildError {
22
+ error: any
23
+ }
24
+
25
+ export type BuildResult = BuildSuccess | BuildError
26
+
27
+ /**
28
+ * Builder is a class that builds, compiles and bundles Svelte components into a nice object for the template handler
29
+ */
30
+ class Builder {
31
+ path: string
32
+ props: Stores
33
+ locals: object
34
+ compiled: {
35
+ client: string | null
36
+ server: string | null
37
+ }
38
+
39
+ ssr: boolean
40
+ workingDir: string
41
+ preprocess: object
42
+
43
+ constructor (
44
+ path: string,
45
+ props: object,
46
+ locals: object,
47
+ client: string | null,
48
+ server: string | null,
49
+ ssr: boolean,
50
+ workingDir: string,
51
+ preprocess: object
52
+ ) {
53
+ this.path = path
54
+ this.props = readable(props)
55
+ this.locals = locals
56
+ this.compiled = {
57
+ client,
58
+ server
59
+ }
60
+ this.ssr = ssr
61
+ this.workingDir = workingDir
62
+ this.preprocess = preprocess
63
+ }
64
+
65
+ async bundle (generate: 'ssr' | 'dom', sveltePath = 'svelte'): Promise<string> {
66
+ const bundle = await esbuild.build({
67
+ entryPoints: [this.path],
68
+ mainFields: ['svelte', 'browser', 'module', 'main'],
69
+ conditions: ['svelte', 'browser'],
70
+ absWorkingDir: this.workingDir,
71
+ write: false,
72
+ outfile: 'component.js',
73
+ bundle: true,
74
+ format: 'esm',
75
+ metafile: true,
76
+ plugins: [
77
+ // @ts-expect-error
78
+ sveltePlugin({
79
+ compilerOptions: {
80
+ generate,
81
+ css: 'injected',
82
+ hydratable: true,
83
+ sveltePath
84
+ },
85
+ preprocess: sveltePreprocess(this.preprocess),
86
+ filterWarnings: (warning: Warning) => {
87
+ if (
88
+ warning.code === 'missing-declaration' &&
89
+ warning.message.includes("'props'")
90
+ ) {
91
+ return false
92
+ }
93
+ return true
94
+ }
95
+ })
96
+ ]
97
+ })
98
+
99
+ // @ts-expect-error
100
+ const throwables = [].concat(bundle.errors, bundle.warnings)
101
+
102
+ if (throwables.length > 0) {
103
+ throw throwables[0] // eslint-disable-line @typescript-eslint/no-throw-literal
104
+ }
105
+
106
+ return bundle.outputFiles[0].text
107
+ }
108
+
109
+ async client (): Promise<string> {
110
+ return (
111
+ this.compiled?.client || (this.standardizeClient(await this.bundle('dom', 'https://esm.sh/svelte'))) // eslint-disable-line
112
+ )
113
+ }
114
+
115
+ standardizeClient (code: string): string {
116
+ const ast = recast.parse(code)
117
+
118
+ let name: string | undefined
119
+
120
+ recast.visit(ast, {
121
+ visitExportNamedDeclaration: (path) => {
122
+ const stagingName: any = path.node?.specifiers?.[0].local?.name
123
+ name = typeof stagingName !== 'string' ? '' : stagingName
124
+ path.prune()
125
+ return false
126
+ }
127
+ })
128
+
129
+ recast.visit(ast, {
130
+ visitIdentifier: (path) => {
131
+ if (path.node.name === name) {
132
+ path.node.name = 'App'
133
+ }
134
+ return false
135
+ }
136
+ })
137
+
138
+ return recast.print(ast).code
139
+ }
140
+
141
+ async server (): Promise<{ output: string, html: string, css: string }> {
142
+ const output = this.compiled?.server || (await this.bundle('ssr')) // eslint-disable-line
143
+
144
+ const Component = importFromStringSync(output, {
145
+ globals: { props: this.props }
146
+ }).default
147
+
148
+ const { html, css } = await Component.render(this.locals)
149
+
150
+ return { output, html, css: css.code }
151
+ }
152
+
153
+ async build (): Promise<BuildResult> {
154
+ try {
155
+ const serv = this.ssr ? await this.server() : null
156
+ const cli = await this.client()
157
+
158
+ const comp = {
159
+ client: cli,
160
+ server: serv?.output
161
+ }
162
+
163
+ return {
164
+ client: cli,
165
+ server: serv,
166
+ compiled: comp
167
+ }
168
+ } catch (e) {
169
+ return { error: e }
170
+ }
171
+ }
172
+ }
173
+
174
+ export default Builder
@@ -0,0 +1,2 @@
1
+ > [!CAUTION]
2
+ > This folder is intended to be built from TypeScript files with `npm run build`
@@ -0,0 +1,42 @@
1
+ import { Stores } from 'svelte/store';
2
+ export interface BuildSuccess {
3
+ client: string;
4
+ server: {
5
+ html: string;
6
+ css: string;
7
+ } | null;
8
+ compiled: {
9
+ client: string;
10
+ server: string | undefined;
11
+ };
12
+ }
13
+ export interface BuildError {
14
+ error: any;
15
+ }
16
+ export type BuildResult = BuildSuccess | BuildError;
17
+ /**
18
+ * Builder is a class that builds, compiles and bundles Svelte components into a nice object for the template handler
19
+ */
20
+ declare class Builder {
21
+ path: string;
22
+ props: Stores;
23
+ locals: object;
24
+ compiled: {
25
+ client: string | null;
26
+ server: string | null;
27
+ };
28
+ ssr: boolean;
29
+ workingDir: string;
30
+ preprocess: object;
31
+ constructor(path: string, props: object, locals: object, client: string | null, server: string | null, ssr: boolean, workingDir: string, preprocess: object);
32
+ bundle(generate: 'ssr' | 'dom', sveltePath?: string): Promise<string>;
33
+ client(): Promise<string>;
34
+ standardizeClient(code: string): string;
35
+ server(): Promise<{
36
+ output: string;
37
+ html: string;
38
+ css: string;
39
+ }>;
40
+ build(): Promise<BuildResult>;
41
+ }
42
+ export default Builder;