@weave-apps/sdk 0.6.0 â 0.8.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 +46 -42
- package/bin/extract-settings-schema.test.js +104 -0
- package/package.json +1 -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
|
@@ -77,28 +77,7 @@ if (!fs.existsSync(tsconfigPath)) {
|
|
|
77
77
|
process.exit(1);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
let esbuildPath = null;
|
|
82
|
-
const possibleEsbuildPaths = [
|
|
83
|
-
path.join(appDir, 'node_modules', '.bin', 'esbuild'),
|
|
84
|
-
path.join(sdkDir, 'node_modules', '.bin', 'esbuild'),
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
for (const candidate of possibleEsbuildPaths) {
|
|
88
|
-
if (fs.existsSync(candidate)) {
|
|
89
|
-
esbuildPath = candidate;
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (!esbuildPath) {
|
|
95
|
-
console.error('â Error: esbuild not found');
|
|
96
|
-
console.error('It should be installed as part of @weave-apps/sdk.');
|
|
97
|
-
console.error('Try running: npm install');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
try {
|
|
80
|
+
async function compile() {
|
|
102
81
|
// Create dist directory if it doesn't exist
|
|
103
82
|
const distDir = path.join(appDir, 'dist');
|
|
104
83
|
if (!fs.existsSync(distDir)) {
|
|
@@ -109,7 +88,7 @@ try {
|
|
|
109
88
|
const targetFile = path.join(distDir, `${appName}.js`);
|
|
110
89
|
|
|
111
90
|
// Step 1: Type-check with tsc (no emit)
|
|
112
|
-
console.log('
|
|
91
|
+
console.log('đ Type-checking with TypeScript...');
|
|
113
92
|
const tscCommand = tscPath === 'npx tsc'
|
|
114
93
|
? `npx tsc --project ${tsconfigPath} --noEmit`
|
|
115
94
|
: `"${tscPath}" --project ${tsconfigPath} --noEmit`;
|
|
@@ -122,25 +101,49 @@ try {
|
|
|
122
101
|
console.log('â
Type-check passed\n');
|
|
123
102
|
|
|
124
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.
|
|
125
107
|
console.log('đĻ Bundling with esbuild...');
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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],
|
|
142
140
|
});
|
|
143
141
|
|
|
142
|
+
if (result.errors.length > 0) {
|
|
143
|
+
console.error('â esbuild errors:', result.errors);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
144
147
|
console.log('â
Bundle complete\n');
|
|
145
148
|
|
|
146
149
|
// Step 3: Extract settings schema and inject into compiled JS
|
|
@@ -160,15 +163,16 @@ try {
|
|
|
160
163
|
|
|
161
164
|
console.log('');
|
|
162
165
|
|
|
163
|
-
// Step 4: Run weave-build to
|
|
166
|
+
// Step 4: Run weave-build to add header comment
|
|
164
167
|
console.log('đ§ Transpiling to Weave format...');
|
|
165
168
|
const buildScript = path.join(__dirname, 'build.js');
|
|
166
169
|
execSync(`node "${buildScript}"`, {
|
|
167
170
|
stdio: 'inherit',
|
|
168
171
|
cwd: appDir
|
|
169
172
|
});
|
|
173
|
+
}
|
|
170
174
|
|
|
171
|
-
|
|
175
|
+
compile().catch((error) => {
|
|
172
176
|
console.error('\nâ Build failed');
|
|
173
177
|
process.exit(1);
|
|
174
|
-
}
|
|
178
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
|
|
89
|
+
it('injects schema when compiling the real heidi-to-dcp mock app with local SDK scripts', () => {
|
|
90
|
+
const mockAppDir = path.resolve(__dirname, '../mocks/heidi-to-dcp');
|
|
91
|
+
const compileScriptPath = path.resolve(__dirname, 'compile.js');
|
|
92
|
+
const outputPath = path.join(mockAppDir, 'dist', 'demo-weave-heidi-dcp.js');
|
|
93
|
+
|
|
94
|
+
execFileSync('node', [compileScriptPath], {
|
|
95
|
+
cwd: mockAppDir,
|
|
96
|
+
stdio: 'pipe',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const output = fs.readFileSync(outputPath, 'utf8');
|
|
100
|
+
assert.match(output, /@weave-settings-schema/);
|
|
101
|
+
assert.match(output, /"key": "DCP_DOMAIN"/);
|
|
102
|
+
assert.match(output, /"key": "HEIDI_CLIENT_NAME_SELECTOR"/);
|
|
103
|
+
});
|
|
104
|
+
});
|
package/package.json
CHANGED
|
@@ -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}`);
|