@weave-apps/sdk 0.5.0 â 0.7.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/build.js +11 -23
- package/bin/compile.js +63 -19
- package/bin/extract-settings-schema.test.js +88 -0
- package/package.json +2 -1
- package/scripts/extract-settings-schema.js +27 -9
package/bin/build.js
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Weave App Builder
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Post-processes the esbuild IIFE bundle for the Weave Platform.
|
|
7
|
+
* Adds a header comment and removes the "use strict" directive.
|
|
8
|
+
*
|
|
9
|
+
* The esbuild IIFE format wraps all code in (() => { ... })() so
|
|
10
|
+
* helper functions and variables stay private to the closure.
|
|
11
|
+
* SDK globals (WeaveBaseApp, weaveDOM, weaveAPI) are resolved
|
|
12
|
+
* inside the closure by the esbuild plugin in compile.js.
|
|
7
13
|
*/
|
|
8
14
|
|
|
9
15
|
const fs = require('fs');
|
|
@@ -29,31 +35,17 @@ const distFile = path.join(appDir, 'dist', `${appName}.js`);
|
|
|
29
35
|
// Check if dist file exists
|
|
30
36
|
if (!fs.existsSync(distFile)) {
|
|
31
37
|
console.error(`â Error: dist/${appName}.js not found`);
|
|
32
|
-
console.error('Run "
|
|
38
|
+
console.error('Run "weave-compile" first to compile TypeScript');
|
|
33
39
|
process.exit(1);
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
console.log('đ¨ Building Weave app...\n');
|
|
37
43
|
|
|
38
|
-
// Read
|
|
44
|
+
// Read esbuild IIFE output
|
|
39
45
|
let code = fs.readFileSync(distFile, 'utf8');
|
|
40
46
|
|
|
41
|
-
// Remove
|
|
42
|
-
code = code.replace(/^
|
|
43
|
-
code = code.replace(/^import .*$/gm, '');
|
|
44
|
-
|
|
45
|
-
// Remove export statements
|
|
46
|
-
code = code.replace(/^export \{[^}]*\};?$/gm, '');
|
|
47
|
-
code = code.replace(/^export /gm, '');
|
|
48
|
-
|
|
49
|
-
// Replace WeaveBaseApp with window.WeaveBaseApp
|
|
50
|
-
code = code.replace(/extends WeaveBaseApp/g, 'extends window.WeaveBaseApp');
|
|
51
|
-
|
|
52
|
-
// Replace weaveDOM with window.weaveDOM (but not if already prefixed with window. or this.)
|
|
53
|
-
code = code.replace(/(?<!window\.)(?<!this\.)weaveDOM\./g, 'window.weaveDOM.');
|
|
54
|
-
|
|
55
|
-
// Replace weaveAPI with window.weaveAPI (but not if already prefixed with window. or this.)
|
|
56
|
-
code = code.replace(/(?<!window\.)(?<!this\.)weaveAPI\./g, 'window.weaveAPI.');
|
|
47
|
+
// Remove "use strict" directive (esbuild adds it, not needed in IIFE)
|
|
48
|
+
code = code.replace(/^"use strict";\n/gm, '');
|
|
57
49
|
|
|
58
50
|
// Remove empty lines created by removals
|
|
59
51
|
code = code.replace(/\n\n\n+/g, '\n\n');
|
|
@@ -65,10 +57,6 @@ const header = `/**
|
|
|
65
57
|
* Built with Weave App SDK
|
|
66
58
|
* Generated: ${new Date().toISOString()}
|
|
67
59
|
*/
|
|
68
|
-
|
|
69
|
-
// Access SDK from window globals
|
|
70
|
-
const { WeaveBaseApp, weaveDOM } = window;
|
|
71
|
-
|
|
72
60
|
`;
|
|
73
61
|
|
|
74
62
|
code = header + code;
|
package/bin/compile.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Weave App Compiler
|
|
5
5
|
*
|
|
6
|
-
* Compiles TypeScript and transpiles to Weave-ready JavaScript
|
|
6
|
+
* Compiles TypeScript and transpiles to Weave-ready JavaScript.
|
|
7
|
+
* Supports multi-file apps by bundling all imports into a single output file.
|
|
8
|
+
*
|
|
9
|
+
* Uses esbuild for bundling and tsc for type-checking only.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
const { execSync } = require('child_process');
|
|
@@ -74,42 +77,82 @@ if (!fs.existsSync(tsconfigPath)) {
|
|
|
74
77
|
process.exit(1);
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
async function compile() {
|
|
78
81
|
// Create dist directory if it doesn't exist
|
|
79
82
|
const distDir = path.join(appDir, 'dist');
|
|
80
83
|
if (!fs.existsSync(distDir)) {
|
|
81
84
|
fs.mkdirSync(distDir, { recursive: true });
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
const entryPoint = path.join(appDir, 'src', 'app.ts');
|
|
88
|
+
const targetFile = path.join(distDir, `${appName}.js`);
|
|
89
|
+
|
|
90
|
+
// Step 1: Type-check with tsc (no emit)
|
|
91
|
+
console.log('đ Type-checking with TypeScript...');
|
|
86
92
|
const tscCommand = tscPath === 'npx tsc'
|
|
87
|
-
? `npx tsc --project ${tsconfigPath} --
|
|
88
|
-
: `"${tscPath}" --project ${tsconfigPath} --
|
|
93
|
+
? `npx tsc --project ${tsconfigPath} --noEmit`
|
|
94
|
+
: `"${tscPath}" --project ${tsconfigPath} --noEmit`;
|
|
89
95
|
|
|
90
96
|
execSync(tscCommand, {
|
|
91
97
|
stdio: 'inherit',
|
|
92
98
|
cwd: appDir
|
|
93
99
|
});
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
console.log('â
Type-check passed\n');
|
|
102
|
+
|
|
103
|
+
// Step 2: Bundle with esbuild (all imports resolved into single file)
|
|
104
|
+
// Uses IIFE format so all code is wrapped in a closure â no global namespace pollution.
|
|
105
|
+
// Helper functions, utilities, etc. stay private inside the closure.
|
|
106
|
+
// Only customElements.define() escapes to register the web component.
|
|
107
|
+
console.log('đĻ Bundling with esbuild...');
|
|
108
|
+
const esbuild = require('esbuild');
|
|
109
|
+
|
|
110
|
+
// Plugin to resolve SDK imports to window globals instead of trying to find the package
|
|
111
|
+
const weaveSDKPlugin = {
|
|
112
|
+
name: 'weave-sdk-globals',
|
|
113
|
+
setup(build) {
|
|
114
|
+
// Intercept imports of the SDK packages
|
|
115
|
+
build.onResolve({ filter: /^@weave-apps\/sdk$|^@weave\/app-sdk$/ }, (args) => {
|
|
116
|
+
return { path: args.path, namespace: 'weave-sdk' };
|
|
117
|
+
});
|
|
118
|
+
// Return a virtual module that re-exports from window globals
|
|
119
|
+
build.onLoad({ filter: /.*/, namespace: 'weave-sdk' }, () => {
|
|
120
|
+
return {
|
|
121
|
+
contents: `
|
|
122
|
+
export const WeaveBaseApp = window.WeaveBaseApp;
|
|
123
|
+
export const weaveDOM = window.weaveDOM;
|
|
124
|
+
export const weaveAPI = window.weaveAPI;
|
|
125
|
+
`,
|
|
126
|
+
loader: 'js',
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const result = await esbuild.build({
|
|
133
|
+
entryPoints: [entryPoint],
|
|
134
|
+
bundle: true,
|
|
135
|
+
outfile: targetFile,
|
|
136
|
+
format: 'iife',
|
|
137
|
+
target: 'es2020',
|
|
138
|
+
platform: 'browser',
|
|
139
|
+
plugins: [weaveSDKPlugin],
|
|
140
|
+
});
|
|
98
141
|
|
|
99
|
-
if (
|
|
100
|
-
|
|
142
|
+
if (result.errors.length > 0) {
|
|
143
|
+
console.error('â esbuild errors:', result.errors);
|
|
144
|
+
process.exit(1);
|
|
101
145
|
}
|
|
102
146
|
|
|
103
|
-
console.log('â
|
|
147
|
+
console.log('â
Bundle complete\n');
|
|
104
148
|
|
|
105
|
-
// Step
|
|
149
|
+
// Step 3: Extract settings schema and inject into compiled JS
|
|
106
150
|
console.log('đ Extracting settings schema...');
|
|
107
151
|
const extractSchemaScript = path.join(sdkDir, 'scripts', 'extract-settings-schema.js');
|
|
108
|
-
const sourceFile = path.join(appDir, 'src', 'app.ts');
|
|
109
152
|
|
|
110
|
-
if (fs.existsSync(extractSchemaScript) && fs.existsSync(
|
|
153
|
+
if (fs.existsSync(extractSchemaScript) && fs.existsSync(entryPoint)) {
|
|
111
154
|
try {
|
|
112
|
-
execSync(`node "${extractSchemaScript}" "${
|
|
155
|
+
execSync(`node "${extractSchemaScript}" "${entryPoint}" "${targetFile}"`, {
|
|
113
156
|
stdio: 'inherit',
|
|
114
157
|
cwd: appDir
|
|
115
158
|
});
|
|
@@ -120,15 +163,16 @@ try {
|
|
|
120
163
|
|
|
121
164
|
console.log('');
|
|
122
165
|
|
|
123
|
-
// Step
|
|
166
|
+
// Step 4: Run weave-build to add header comment
|
|
124
167
|
console.log('đ§ Transpiling to Weave format...');
|
|
125
168
|
const buildScript = path.join(__dirname, 'build.js');
|
|
126
169
|
execSync(`node "${buildScript}"`, {
|
|
127
170
|
stdio: 'inherit',
|
|
128
171
|
cwd: appDir
|
|
129
172
|
});
|
|
173
|
+
}
|
|
130
174
|
|
|
131
|
-
|
|
175
|
+
compile().catch((error) => {
|
|
132
176
|
console.error('\nâ Build failed');
|
|
133
177
|
process.exit(1);
|
|
134
|
-
}
|
|
178
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const { describe, it } = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execFileSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const scriptPath = path.resolve(__dirname, '../scripts/extract-settings-schema.js');
|
|
9
|
+
|
|
10
|
+
function createTempFiles(jsContent) {
|
|
11
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'weave-extract-settings-'));
|
|
12
|
+
const tsPath = path.join(tmpDir, 'app.ts');
|
|
13
|
+
const jsPath = path.join(tmpDir, 'bundle.js');
|
|
14
|
+
|
|
15
|
+
const tsContent = `
|
|
16
|
+
import { WeaveBaseApp } from '@weave-apps/sdk';
|
|
17
|
+
|
|
18
|
+
interface AppSettings {
|
|
19
|
+
/** @description Example domain */
|
|
20
|
+
/** @default "https://example.com" */
|
|
21
|
+
DCP_DOMAIN?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class DemoApp extends WeaveBaseApp<AppSettings, Record<string, never>> {
|
|
25
|
+
constructor() {
|
|
26
|
+
super({ id: 'demo', name: 'Demo', version: '1.0.0' });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
fs.writeFileSync(tsPath, tsContent, 'utf8');
|
|
32
|
+
fs.writeFileSync(jsPath, jsContent, 'utf8');
|
|
33
|
+
|
|
34
|
+
return { tmpDir, tsPath, jsPath };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function runScript(tsPath, jsPath) {
|
|
38
|
+
execFileSync('node', [scriptPath, tsPath, jsPath], { stdio: 'pipe' });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('extract-settings-schema script', () => {
|
|
42
|
+
it('injects schema for class declarations extending window.WeaveBaseApp', () => {
|
|
43
|
+
const { tmpDir, tsPath, jsPath } = createTempFiles(
|
|
44
|
+
`class DemoApp extends window.WeaveBaseApp { constructor() {} }`
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
runScript(tsPath, jsPath);
|
|
49
|
+
const output = fs.readFileSync(jsPath, 'utf8');
|
|
50
|
+
assert.match(output, /@weave-settings-schema/);
|
|
51
|
+
assert.match(output, /class DemoApp extends window\.WeaveBaseApp/);
|
|
52
|
+
assert.match(output, /"key": "DCP_DOMAIN"/);
|
|
53
|
+
} finally {
|
|
54
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('injects schema for assigned anonymous classes in bundled output', () => {
|
|
59
|
+
const { tmpDir, tsPath, jsPath } = createTempFiles(
|
|
60
|
+
`var DemoApp = class extends window.WeaveBaseApp { constructor() {} };`
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
runScript(tsPath, jsPath);
|
|
65
|
+
const output = fs.readFileSync(jsPath, 'utf8');
|
|
66
|
+
assert.match(output, /@weave-settings-schema/);
|
|
67
|
+
assert.match(output, /var DemoApp = class extends window\.WeaveBaseApp/);
|
|
68
|
+
assert.match(output, /"defaultValue": "https:\/\/example.com"/);
|
|
69
|
+
} finally {
|
|
70
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('does not inject twice when schema already exists', () => {
|
|
75
|
+
const { tmpDir, tsPath, jsPath } = createTempFiles(
|
|
76
|
+
`/**\n * @weave-settings-schema []\n */\nvar DemoApp = class extends window.WeaveBaseApp { constructor() {} };`
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
runScript(tsPath, jsPath);
|
|
81
|
+
const output = fs.readFileSync(jsPath, 'utf8');
|
|
82
|
+
const schemaOccurrences = (output.match(/@weave-settings-schema/g) || []).length;
|
|
83
|
+
assert.equal(schemaOccurrences, 1);
|
|
84
|
+
} finally {
|
|
85
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weave-apps/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "SDK for building Weave Micro Apps",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"author": "Weave Platform",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"esbuild": "^0.24.0",
|
|
35
36
|
"typescript": "^5.9.3"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
@@ -173,14 +173,34 @@ function injectSchemaIntoJS(jsFilePath, schema) {
|
|
|
173
173
|
|
|
174
174
|
let jsCode = fs.readFileSync(jsFilePath, 'utf-8');
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (!classMatch) {
|
|
179
|
-
console.warn('â ī¸ Could not find class extending WeaveBaseApp');
|
|
176
|
+
if (jsCode.includes('@weave-settings-schema')) {
|
|
177
|
+
console.log('âšī¸ Settings schema already present, skipping injection');
|
|
180
178
|
return;
|
|
181
179
|
}
|
|
182
180
|
|
|
183
|
-
|
|
181
|
+
// Find app class definitions in both source-like and bundled outputs.
|
|
182
|
+
// Examples:
|
|
183
|
+
// class MyApp extends WeaveBaseApp
|
|
184
|
+
// class MyApp extends window.WeaveBaseApp
|
|
185
|
+
// var MyApp = class extends window.WeaveBaseApp
|
|
186
|
+
const classDefinitionPatterns = [
|
|
187
|
+
/(?:var|let|const)\s+\w+\s*=\s*class(?:\s+\w+)?\s+extends\s+(?:window\.)?WeaveBaseApp\b/,
|
|
188
|
+
/class\s+\w+\s+extends\s+(?:window\.)?WeaveBaseApp\b/,
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
let classDefinition = null;
|
|
192
|
+
for (const pattern of classDefinitionPatterns) {
|
|
193
|
+
const match = jsCode.match(pattern);
|
|
194
|
+
if (match) {
|
|
195
|
+
classDefinition = match[0];
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!classDefinition) {
|
|
201
|
+
console.warn('â ī¸ Could not find class extending WeaveBaseApp');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
184
204
|
|
|
185
205
|
// Create JSDoc comment with schema
|
|
186
206
|
const schemaComment = `
|
|
@@ -189,10 +209,8 @@ function injectSchemaIntoJS(jsFilePath, schema) {
|
|
|
189
209
|
*/`;
|
|
190
210
|
|
|
191
211
|
// Inject before class definition
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`${schemaComment}\n$1`
|
|
195
|
-
);
|
|
212
|
+
const classDefinitionIndex = jsCode.indexOf(classDefinition);
|
|
213
|
+
jsCode = `${jsCode.slice(0, classDefinitionIndex)}${schemaComment}\n${jsCode.slice(classDefinitionIndex)}`;
|
|
196
214
|
|
|
197
215
|
fs.writeFileSync(jsFilePath, jsCode, 'utf-8');
|
|
198
216
|
console.log(`â
Injected settings schema into ${jsFilePath}`);
|