@qt-test/apex-dsl-compiler 0.1.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/README.md ADDED
@@ -0,0 +1,272 @@
1
+ # @interswitch/apex-compiler
2
+
3
+ > AXML/ACSS compiler for APEX mini-app platform
4
+
5
+ This package compiles APEX DSL (AXML templates and ACSS styles) into JavaScript code that works with the APEX runtime.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @interswitch/apex-compiler
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Programmatic API
16
+
17
+ ```javascript
18
+ import { compile, compileAXML, compileACSS } from '@interswitch/apex-compiler';
19
+
20
+ // Compile a complete page/component
21
+ const result = compile({
22
+ axml: '<view class="container"><text>{{message}}</text></view>',
23
+ acss: '.container { padding: 20rpx; }',
24
+ json: { component: true }
25
+ });
26
+
27
+ console.log(result.code);
28
+ console.log(result.styles);
29
+
30
+ // Compile AXML only
31
+ const template = compileAXML('<view a:for="{{items}}">{{item}}</view>');
32
+ console.log(template.render);
33
+
34
+ // Compile ACSS only
35
+ const styles = compileACSS('.btn { width: 200rpx; color: #333; }');
36
+ console.log(styles.css);
37
+ ```
38
+
39
+ ### CLI
40
+
41
+ ```bash
42
+ # Compile a single file
43
+ apexc compile pages/index/index.axml -o dist/
44
+
45
+ # Compile a directory
46
+ apexc compile src/ -o dist/
47
+
48
+ # Watch mode
49
+ apexc compile src/ -o dist/ --watch
50
+ ```
51
+
52
+ ## AXML Syntax
53
+
54
+ ### Basic Elements
55
+
56
+ ```xml
57
+ <!-- View container -->
58
+ <view class="container">
59
+ <text>Hello World</text>
60
+ </view>
61
+
62
+ <!-- Data binding -->
63
+ <text>{{message}}</text>
64
+ <view class="item-{{index}}">{{item.name}}</view>
65
+
66
+ <!-- Attribute binding -->
67
+ <image src="{{imageUrl}}" mode="aspectFit" />
68
+ ```
69
+
70
+ ### Directives
71
+
72
+ ```xml
73
+ <!-- Conditional rendering -->
74
+ <view a:if="{{condition}}">Shown if true</view>
75
+ <view a:elif="{{other}}">Else if</view>
76
+ <view a:else>Else</view>
77
+
78
+ <!-- List rendering -->
79
+ <view a:for="{{items}}" a:for-item="item" a:for-index="idx">
80
+ {{idx}}: {{item.name}}
81
+ </view>
82
+
83
+ <!-- Key for optimization -->
84
+ <view a:for="{{items}}" a:key="id">{{item.name}}</view>
85
+ ```
86
+
87
+ ### Event Binding
88
+
89
+ ```xml
90
+ <!-- Tap event -->
91
+ <button bindtap="handleTap">Click me</button>
92
+
93
+ <!-- With event object -->
94
+ <input bindinput="handleInput" />
95
+
96
+ <!-- Catch (stop propagation) -->
97
+ <view catchtap="handleTap">Won't bubble</view>
98
+
99
+ <!-- Pass data -->
100
+ <button bindtap="handleTap" data-id="{{item.id}}">{{item.name}}</button>
101
+ ```
102
+
103
+ ### Built-in Components
104
+
105
+ ```xml
106
+ <!-- View -->
107
+ <view class="container" style="padding: 10px;">Content</view>
108
+
109
+ <!-- Text -->
110
+ <text selectable="{{true}}">Selectable text</text>
111
+
112
+ <!-- Image -->
113
+ <image src="/images/logo.png" mode="aspectFit" lazy-load />
114
+
115
+ <!-- Button -->
116
+ <button type="primary" loading="{{isLoading}}">Submit</button>
117
+
118
+ <!-- Input -->
119
+ <input type="text" placeholder="Enter name" value="{{name}}" bindinput="onInput" />
120
+
121
+ <!-- Scroll View -->
122
+ <scroll-view scroll-y style="height: 300px;">
123
+ <view a:for="{{items}}">{{item}}</view>
124
+ </scroll-view>
125
+
126
+ <!-- Swiper -->
127
+ <swiper autoplay circular>
128
+ <swiper-item a:for="{{banners}}">
129
+ <image src="{{item.url}}" />
130
+ </swiper-item>
131
+ </swiper>
132
+ ```
133
+
134
+ ### Templates & Slots
135
+
136
+ ```xml
137
+ <!-- Define template -->
138
+ <template name="userCard">
139
+ <view class="card">
140
+ <text>{{name}}</text>
141
+ <text>{{age}}</text>
142
+ </view>
143
+ </template>
144
+
145
+ <!-- Use template -->
146
+ <template is="userCard" data="{{...user}}" />
147
+
148
+ <!-- Slots in components -->
149
+ <my-component>
150
+ <view slot="header">Header content</view>
151
+ <view>Default slot content</view>
152
+ <view slot="footer">Footer content</view>
153
+ </my-component>
154
+ ```
155
+
156
+ ## ACSS Syntax
157
+
158
+ ACSS is CSS with extensions for mini-app development.
159
+
160
+ ### RPX Units
161
+
162
+ ```css
163
+ /* rpx = responsive pixel, auto-scales with screen width */
164
+ /* 750rpx = full screen width on any device */
165
+ .container {
166
+ width: 750rpx;
167
+ padding: 20rpx;
168
+ font-size: 28rpx;
169
+ }
170
+ ```
171
+
172
+ ### Standard CSS
173
+
174
+ ```css
175
+ .button {
176
+ background-color: #1890ff;
177
+ color: white;
178
+ border-radius: 8rpx;
179
+ padding: 16rpx 32rpx;
180
+ }
181
+
182
+ .button:active {
183
+ opacity: 0.8;
184
+ }
185
+ ```
186
+
187
+ ### Import
188
+
189
+ ```css
190
+ @import './common.acss';
191
+ @import '/styles/theme.acss';
192
+ ```
193
+
194
+ ### Variables (CSS Custom Properties)
195
+
196
+ ```css
197
+ :root {
198
+ --primary-color: #1890ff;
199
+ --text-color: #333;
200
+ }
201
+
202
+ .button {
203
+ background: var(--primary-color);
204
+ color: var(--text-color);
205
+ }
206
+ ```
207
+
208
+ ## Compiler Output
209
+
210
+ ### Input
211
+
212
+ ```xml
213
+ <!-- index.axml -->
214
+ <view class="page">
215
+ <text class="title">{{title}}</text>
216
+ <button bindtap="handleClick">Click</button>
217
+ </view>
218
+ ```
219
+
220
+ ### Output (simplified)
221
+
222
+ ```javascript
223
+ export function render(_ctx) {
224
+ return h('view', { class: 'page' }, [
225
+ h('text', { class: 'title' }, [_ctx.title]),
226
+ h('button', { bindtap: _ctx.handleClick }, ['Click'])
227
+ ]);
228
+ }
229
+ ```
230
+
231
+ ## Architecture
232
+
233
+ ```
234
+ ┌─────────────────────────────────────────────────────────┐
235
+ │ Source Files │
236
+ │ (.axml, .acss, .json, .js) │
237
+ └─────────────────────────────────────────────────────────┘
238
+
239
+
240
+ ┌─────────────────────────────────────────────────────────┐
241
+ │ Lexer │
242
+ │ (Tokenize source code) │
243
+ └─────────────────────────────────────────────────────────┘
244
+
245
+
246
+ ┌─────────────────────────────────────────────────────────┐
247
+ │ Parser │
248
+ │ (Build Abstract Syntax Tree) │
249
+ └─────────────────────────────────────────────────────────┘
250
+
251
+
252
+ ┌─────────────────────────────────────────────────────────┐
253
+ │ Transformer │
254
+ │ (Optimize, validate, transform AST) │
255
+ └─────────────────────────────────────────────────────────┘
256
+
257
+
258
+ ┌─────────────────────────────────────────────────────────┐
259
+ │ Code Generator │
260
+ │ (Generate JavaScript output) │
261
+ └─────────────────────────────────────────────────────────┘
262
+
263
+
264
+ ┌─────────────────────────────────────────────────────────┐
265
+ │ Output Files │
266
+ │ (.js, .css, .json) │
267
+ └─────────────────────────────────────────────────────────┘
268
+ ```
269
+
270
+ ## License
271
+
272
+ MIT
package/bin/apexc.js ADDED
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * APEX Compiler CLI
5
+ *
6
+ * Compiles AXML/ACSS files into JavaScript
7
+ *
8
+ * Usage:
9
+ * apexc <input> [options]
10
+ * apexc src/pages/index --outDir dist
11
+ * apexc src/components/button -o dist/button.js
12
+ *
13
+ * Options:
14
+ * -o, --output <file> Output file
15
+ * --outDir <dir> Output directory
16
+ * -w, --watch Watch mode
17
+ * -m, --minify Minify output
18
+ * --sourcemap Generate source maps
19
+ * --target <target> Target ES version (es2015, es2020, esnext)
20
+ * --component Compile as component (default: auto-detect)
21
+ * --page Compile as page
22
+ * -h, --help Show help
23
+ * -v, --version Show version
24
+ */
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+
29
+ // Parse arguments
30
+ const args = process.argv.slice(2);
31
+ const options = {
32
+ input: null,
33
+ output: null,
34
+ outDir: null,
35
+ watch: false,
36
+ minify: false,
37
+ sourcemap: false,
38
+ target: 'es2020',
39
+ isComponent: null,
40
+ };
41
+
42
+ let i = 0;
43
+ while (i < args.length) {
44
+ const arg = args[i];
45
+
46
+ switch (arg) {
47
+ case '-o':
48
+ case '--output':
49
+ options.output = args[++i];
50
+ break;
51
+
52
+ case '--outDir':
53
+ options.outDir = args[++i];
54
+ break;
55
+
56
+ case '-w':
57
+ case '--watch':
58
+ options.watch = true;
59
+ break;
60
+
61
+ case '-m':
62
+ case '--minify':
63
+ options.minify = true;
64
+ break;
65
+
66
+ case '--sourcemap':
67
+ options.sourcemap = true;
68
+ break;
69
+
70
+ case '--target':
71
+ options.target = args[++i];
72
+ break;
73
+
74
+ case '--component':
75
+ options.isComponent = true;
76
+ break;
77
+
78
+ case '--page':
79
+ options.isComponent = false;
80
+ break;
81
+
82
+ case '-h':
83
+ case '--help':
84
+ showHelp();
85
+ process.exit(0);
86
+
87
+ case '-v':
88
+ case '--version':
89
+ showVersion();
90
+ process.exit(0);
91
+
92
+ default:
93
+ if (arg.startsWith('-')) {
94
+ console.error(`Unknown option: ${arg}`);
95
+ process.exit(1);
96
+ }
97
+ options.input = arg;
98
+ }
99
+
100
+ i++;
101
+ }
102
+
103
+ function showHelp() {
104
+ console.log(`
105
+ APEX Compiler - Compiles AXML/ACSS to JavaScript
106
+
107
+ Usage:
108
+ apexc <input> [options]
109
+
110
+ Examples:
111
+ apexc src/pages/index --outDir dist
112
+ apexc src/components/button -o dist/button.js
113
+ apexc src/pages/home -m --sourcemap
114
+
115
+ Arguments:
116
+ <input> Input file or directory (without extension)
117
+ Will look for .axml, .acss, .json, .js files
118
+
119
+ Options:
120
+ -o, --output <file> Output file path
121
+ --outDir <dir> Output directory (preserves structure)
122
+ -w, --watch Watch files for changes
123
+ -m, --minify Minify the output
124
+ --sourcemap Generate source maps
125
+ --target <version> Target ES version: es2015, es2020, esnext (default: es2020)
126
+ --component Force compilation as component
127
+ --page Force compilation as page
128
+ -h, --help Show this help message
129
+ -v, --version Show version number
130
+
131
+ File Resolution:
132
+ Given input "src/pages/index", the compiler looks for:
133
+ - src/pages/index.axml (template)
134
+ - src/pages/index.acss (styles)
135
+ - src/pages/index.json (config)
136
+ - src/pages/index.js (logic)
137
+
138
+ At minimum, one of .axml or .js must exist.
139
+ `);
140
+ }
141
+
142
+ function showVersion() {
143
+ try {
144
+ const pkg = require('../package.json');
145
+ console.log(`apexc version ${pkg.version}`);
146
+ } catch {
147
+ console.log('apexc version 0.1.0');
148
+ }
149
+ }
150
+
151
+ async function main() {
152
+ if (!options.input) {
153
+ console.error('Error: No input file specified');
154
+ console.error('Run "apexc --help" for usage information');
155
+ process.exit(1);
156
+ }
157
+
158
+ // Resolve input path
159
+ const inputBase = path.resolve(options.input);
160
+ const inputDir = path.dirname(inputBase);
161
+ const inputName = path.basename(inputBase, path.extname(inputBase));
162
+
163
+ // Find input files
164
+ const files = {
165
+ axml: findFile(inputDir, inputName, '.axml'),
166
+ acss: findFile(inputDir, inputName, '.acss'),
167
+ json: findFile(inputDir, inputName, '.json'),
168
+ js: findFile(inputDir, inputName, '.js'),
169
+ };
170
+
171
+ if (!files.axml && !files.js) {
172
+ console.error(`Error: No .axml or .js file found for "${options.input}"`);
173
+ process.exit(1);
174
+ }
175
+
176
+ // Load the compiler
177
+ let compile;
178
+ try {
179
+ const compiler = require('../dist/index.js');
180
+ compile = compiler.compile;
181
+ } catch (e) {
182
+ console.error('Error: Compiler not built. Run "npm run build" first.');
183
+ console.error(e.message);
184
+ process.exit(1);
185
+ }
186
+
187
+ // Compile function
188
+ async function compileFiles() {
189
+ const startTime = Date.now();
190
+
191
+ // Read input files
192
+ const sources = {
193
+ axml: files.axml ? fs.readFileSync(files.axml, 'utf-8') : undefined,
194
+ acss: files.acss ? fs.readFileSync(files.acss, 'utf-8') : undefined,
195
+ json: files.json ? fs.readFileSync(files.json, 'utf-8') : undefined,
196
+ js: files.js ? fs.readFileSync(files.js, 'utf-8') : undefined,
197
+ };
198
+
199
+ // Determine if component
200
+ let isComponent = options.isComponent;
201
+ if (isComponent === null && sources.json) {
202
+ try {
203
+ const config = JSON.parse(sources.json);
204
+ isComponent = config.component === true;
205
+ } catch {
206
+ isComponent = false;
207
+ }
208
+ }
209
+ isComponent = isComponent ?? false;
210
+
211
+ // Compile
212
+ const result = compile({
213
+ axml: sources.axml,
214
+ acss: sources.acss,
215
+ json: sources.json,
216
+ js: sources.js,
217
+ filename: inputName,
218
+ sourceMap: options.sourcemap,
219
+ minify: options.minify,
220
+ target: options.target,
221
+ isComponent,
222
+ });
223
+
224
+ // Report errors
225
+ if (result.errors.length > 0) {
226
+ console.error('Compilation errors:');
227
+ for (const error of result.errors) {
228
+ console.error(` [${error.code}] ${error.message}`);
229
+ }
230
+ return false;
231
+ }
232
+
233
+ // Report warnings
234
+ if (result.warnings.length > 0) {
235
+ console.warn('Warnings:');
236
+ for (const warning of result.warnings) {
237
+ console.warn(` ${warning.message}`);
238
+ }
239
+ }
240
+
241
+ // Determine output path
242
+ let outputPath;
243
+ if (options.output) {
244
+ outputPath = path.resolve(options.output);
245
+ } else if (options.outDir) {
246
+ const relPath = path.relative(process.cwd(), inputBase);
247
+ outputPath = path.join(options.outDir, relPath + '.js');
248
+ } else {
249
+ outputPath = inputBase + '.compiled.js';
250
+ }
251
+
252
+ // Ensure output directory exists
253
+ const outputDir = path.dirname(outputPath);
254
+ if (!fs.existsSync(outputDir)) {
255
+ fs.mkdirSync(outputDir, { recursive: true });
256
+ }
257
+
258
+ // Write output
259
+ let output = result.code;
260
+ if (options.sourcemap && result.map) {
261
+ const mapPath = outputPath + '.map';
262
+ fs.writeFileSync(mapPath, JSON.stringify(result.map, null, 2));
263
+ output += `\n//# sourceMappingURL=${path.basename(mapPath)}`;
264
+ }
265
+ fs.writeFileSync(outputPath, output);
266
+
267
+ // Write CSS if present
268
+ if (result.css) {
269
+ const cssPath = outputPath.replace(/\.js$/, '.css');
270
+ fs.writeFileSync(cssPath, result.css);
271
+ }
272
+
273
+ const elapsed = Date.now() - startTime;
274
+ console.log(`Compiled ${inputName} -> ${path.relative(process.cwd(), outputPath)} (${elapsed}ms)`);
275
+
276
+ return true;
277
+ }
278
+
279
+ // Initial compile
280
+ const success = await compileFiles();
281
+
282
+ // Watch mode
283
+ if (options.watch) {
284
+ console.log('\nWatching for changes...');
285
+
286
+ const watchFiles = Object.values(files).filter(Boolean);
287
+ for (const file of watchFiles) {
288
+ fs.watch(file, { persistent: true }, async (eventType) => {
289
+ if (eventType === 'change') {
290
+ console.log(`\nFile changed: ${path.basename(file)}`);
291
+ await compileFiles();
292
+ }
293
+ });
294
+ }
295
+ } else if (!success) {
296
+ process.exit(1);
297
+ }
298
+ }
299
+
300
+ function findFile(dir, name, ext) {
301
+ const filePath = path.join(dir, name + ext);
302
+ return fs.existsSync(filePath) ? filePath : null;
303
+ }
304
+
305
+ main().catch((error) => {
306
+ console.error('Fatal error:', error.message);
307
+ process.exit(1);
308
+ });
@@ -0,0 +1,67 @@
1
+ import { h as ACSSStylesheet } from '../ast-DEVgojMx.mjs';
2
+ export { A as ACSSDeclaration, a as ACSSImport, b as ACSSKeyframe, c as ACSSKeyframesRule, d as ACSSMediaRule, e as ACSSRule, f as ACSSSelector, g as ACSSStyleRule } from '../ast-DEVgojMx.mjs';
3
+
4
+ /**
5
+ * ACSS Parser
6
+ *
7
+ * Tokenizes and parses ACSS stylesheets into an AST
8
+ */
9
+
10
+ /**
11
+ * Parses ACSS source into an AST
12
+ */
13
+ declare function parseACSS(source: string): {
14
+ ast: ACSSStylesheet;
15
+ errors: string[];
16
+ };
17
+
18
+ /**
19
+ * ACSS Compiler
20
+ *
21
+ * Transforms ACSS AST into optimized CSS
22
+ */
23
+
24
+ /**
25
+ * Compile options
26
+ */
27
+ interface ACSSCompileOptions {
28
+ /** Source filename for error reporting */
29
+ filename?: string;
30
+ /** Minify output */
31
+ minify?: boolean;
32
+ /** Add vendor prefixes */
33
+ autoprefixer?: boolean;
34
+ /** Scope styles to component */
35
+ scoped?: boolean;
36
+ /** Scope ID for scoped styles */
37
+ scopeId?: string;
38
+ /** Enable rpx to rem conversion */
39
+ rpxToRem?: boolean;
40
+ /** Base font size for rpx conversion (default: 16) */
41
+ baseFontSize?: number;
42
+ /** Design width for rpx (default: 750) */
43
+ designWidth?: number;
44
+ }
45
+ /**
46
+ * Compile result
47
+ */
48
+ interface ACSSCompileResult {
49
+ /** Generated CSS */
50
+ css: string;
51
+ /** The AST (for debugging/tooling) */
52
+ ast: ACSSStylesheet;
53
+ /** Compilation warnings */
54
+ warnings: string[];
55
+ /** Compilation errors */
56
+ errors: string[];
57
+ /** Import paths */
58
+ imports: string[];
59
+ /** Used animations */
60
+ animations: Set<string>;
61
+ }
62
+ /**
63
+ * Compiles ACSS source into CSS
64
+ */
65
+ declare function compileACSS(source: string, options?: ACSSCompileOptions): ACSSCompileResult;
66
+
67
+ export { type ACSSCompileOptions, type ACSSCompileResult, ACSSStylesheet, compileACSS, parseACSS };
@@ -0,0 +1,67 @@
1
+ import { h as ACSSStylesheet } from '../ast-DEVgojMx.js';
2
+ export { A as ACSSDeclaration, a as ACSSImport, b as ACSSKeyframe, c as ACSSKeyframesRule, d as ACSSMediaRule, e as ACSSRule, f as ACSSSelector, g as ACSSStyleRule } from '../ast-DEVgojMx.js';
3
+
4
+ /**
5
+ * ACSS Parser
6
+ *
7
+ * Tokenizes and parses ACSS stylesheets into an AST
8
+ */
9
+
10
+ /**
11
+ * Parses ACSS source into an AST
12
+ */
13
+ declare function parseACSS(source: string): {
14
+ ast: ACSSStylesheet;
15
+ errors: string[];
16
+ };
17
+
18
+ /**
19
+ * ACSS Compiler
20
+ *
21
+ * Transforms ACSS AST into optimized CSS
22
+ */
23
+
24
+ /**
25
+ * Compile options
26
+ */
27
+ interface ACSSCompileOptions {
28
+ /** Source filename for error reporting */
29
+ filename?: string;
30
+ /** Minify output */
31
+ minify?: boolean;
32
+ /** Add vendor prefixes */
33
+ autoprefixer?: boolean;
34
+ /** Scope styles to component */
35
+ scoped?: boolean;
36
+ /** Scope ID for scoped styles */
37
+ scopeId?: string;
38
+ /** Enable rpx to rem conversion */
39
+ rpxToRem?: boolean;
40
+ /** Base font size for rpx conversion (default: 16) */
41
+ baseFontSize?: number;
42
+ /** Design width for rpx (default: 750) */
43
+ designWidth?: number;
44
+ }
45
+ /**
46
+ * Compile result
47
+ */
48
+ interface ACSSCompileResult {
49
+ /** Generated CSS */
50
+ css: string;
51
+ /** The AST (for debugging/tooling) */
52
+ ast: ACSSStylesheet;
53
+ /** Compilation warnings */
54
+ warnings: string[];
55
+ /** Compilation errors */
56
+ errors: string[];
57
+ /** Import paths */
58
+ imports: string[];
59
+ /** Used animations */
60
+ animations: Set<string>;
61
+ }
62
+ /**
63
+ * Compiles ACSS source into CSS
64
+ */
65
+ declare function compileACSS(source: string, options?: ACSSCompileOptions): ACSSCompileResult;
66
+
67
+ export { type ACSSCompileOptions, type ACSSCompileResult, ACSSStylesheet, compileACSS, parseACSS };