pulse-js-framework 1.0.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/LICENSE +21 -0
- package/README.md +182 -0
- package/cli/build.js +199 -0
- package/cli/dev.js +225 -0
- package/cli/index.js +324 -0
- package/compiler/index.js +65 -0
- package/compiler/lexer.js +581 -0
- package/compiler/parser.js +900 -0
- package/compiler/transformer.js +552 -0
- package/index.js +19 -0
- package/loader/vite-plugin.js +160 -0
- package/package.json +58 -0
- package/runtime/dom.js +484 -0
- package/runtime/index.js +13 -0
- package/runtime/pulse.js +339 -0
- package/runtime/router.js +392 -0
- package/runtime/store.js +301 -0
package/cli/index.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pulse CLI - Command line interface
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname, join, resolve } from 'path';
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, cpSync } from 'fs';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
const VERSION = '1.0.0';
|
|
15
|
+
|
|
16
|
+
// Command handlers
|
|
17
|
+
const commands = {
|
|
18
|
+
help: showHelp,
|
|
19
|
+
version: showVersion,
|
|
20
|
+
create: createProject,
|
|
21
|
+
dev: runDev,
|
|
22
|
+
build: runBuild,
|
|
23
|
+
compile: compileFile
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Main entry point
|
|
28
|
+
*/
|
|
29
|
+
async function main() {
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
const command = args[0] || 'help';
|
|
32
|
+
|
|
33
|
+
if (command in commands) {
|
|
34
|
+
await commands[command](args.slice(1));
|
|
35
|
+
} else {
|
|
36
|
+
console.error(`Unknown command: ${command}`);
|
|
37
|
+
console.log('Run "pulse help" for usage information.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Show help message
|
|
44
|
+
*/
|
|
45
|
+
function showHelp() {
|
|
46
|
+
console.log(`
|
|
47
|
+
Pulse Framework CLI v${VERSION}
|
|
48
|
+
|
|
49
|
+
Usage: pulse <command> [options]
|
|
50
|
+
|
|
51
|
+
Commands:
|
|
52
|
+
create <name> Create a new Pulse project
|
|
53
|
+
dev Start development server
|
|
54
|
+
build Build for production
|
|
55
|
+
compile <file> Compile a .pulse file to JavaScript
|
|
56
|
+
version Show version number
|
|
57
|
+
help Show this help message
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
pulse create my-app
|
|
61
|
+
pulse dev
|
|
62
|
+
pulse build
|
|
63
|
+
pulse compile src/App.pulse
|
|
64
|
+
|
|
65
|
+
Documentation: https://github.com/pulse-framework/pulse
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Show version
|
|
71
|
+
*/
|
|
72
|
+
function showVersion() {
|
|
73
|
+
console.log(`Pulse Framework v${VERSION}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create a new project
|
|
78
|
+
*/
|
|
79
|
+
async function createProject(args) {
|
|
80
|
+
const projectName = args[0];
|
|
81
|
+
|
|
82
|
+
if (!projectName) {
|
|
83
|
+
console.error('Please provide a project name.');
|
|
84
|
+
console.log('Usage: pulse create <project-name>');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const projectPath = resolve(process.cwd(), projectName);
|
|
89
|
+
|
|
90
|
+
if (existsSync(projectPath)) {
|
|
91
|
+
console.error(`Directory "${projectName}" already exists.`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(`Creating new Pulse project: ${projectName}`);
|
|
96
|
+
|
|
97
|
+
// Create project structure
|
|
98
|
+
mkdirSync(projectPath);
|
|
99
|
+
mkdirSync(join(projectPath, 'src'));
|
|
100
|
+
mkdirSync(join(projectPath, 'public'));
|
|
101
|
+
|
|
102
|
+
// Create package.json
|
|
103
|
+
const packageJson = {
|
|
104
|
+
name: projectName,
|
|
105
|
+
version: '0.1.0',
|
|
106
|
+
type: 'module',
|
|
107
|
+
scripts: {
|
|
108
|
+
dev: 'pulse dev',
|
|
109
|
+
build: 'pulse build',
|
|
110
|
+
preview: 'vite preview'
|
|
111
|
+
},
|
|
112
|
+
dependencies: {
|
|
113
|
+
'pulse-js-framework': '^1.0.0'
|
|
114
|
+
},
|
|
115
|
+
devDependencies: {
|
|
116
|
+
vite: '^5.0.0'
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
writeFileSync(
|
|
121
|
+
join(projectPath, 'package.json'),
|
|
122
|
+
JSON.stringify(packageJson, null, 2)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Create vite.config.js
|
|
126
|
+
const viteConfig = `import { defineConfig } from 'vite';
|
|
127
|
+
import pulse from 'pulse-js-framework/vite';
|
|
128
|
+
|
|
129
|
+
export default defineConfig({
|
|
130
|
+
plugins: [pulse()]
|
|
131
|
+
});
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
writeFileSync(join(projectPath, 'vite.config.js'), viteConfig);
|
|
135
|
+
|
|
136
|
+
// Create index.html
|
|
137
|
+
const indexHtml = `<!DOCTYPE html>
|
|
138
|
+
<html lang="en">
|
|
139
|
+
<head>
|
|
140
|
+
<meta charset="UTF-8">
|
|
141
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
142
|
+
<title>${projectName}</title>
|
|
143
|
+
</head>
|
|
144
|
+
<body>
|
|
145
|
+
<div id="app"></div>
|
|
146
|
+
<script type="module" src="/src/main.js"></script>
|
|
147
|
+
</body>
|
|
148
|
+
</html>
|
|
149
|
+
`;
|
|
150
|
+
|
|
151
|
+
writeFileSync(join(projectPath, 'index.html'), indexHtml);
|
|
152
|
+
|
|
153
|
+
// Create main.js
|
|
154
|
+
const mainJs = `import App from './App.pulse';
|
|
155
|
+
|
|
156
|
+
App.mount('#app');
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
writeFileSync(join(projectPath, 'src', 'main.js'), mainJs);
|
|
160
|
+
|
|
161
|
+
// Create App.pulse
|
|
162
|
+
const appPulse = `@page App
|
|
163
|
+
|
|
164
|
+
state {
|
|
165
|
+
count: 0
|
|
166
|
+
name: "Pulse"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
view {
|
|
170
|
+
#app {
|
|
171
|
+
h1.title "Welcome to {name}!"
|
|
172
|
+
|
|
173
|
+
.counter {
|
|
174
|
+
p "Count: {count}"
|
|
175
|
+
|
|
176
|
+
.buttons {
|
|
177
|
+
button @click(count--) "-"
|
|
178
|
+
button @click(count++) "+"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
p.info "Edit src/App.pulse and save to hot reload."
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
style {
|
|
187
|
+
#app {
|
|
188
|
+
font-family: system-ui, -apple-system, sans-serif
|
|
189
|
+
max-width: 600px
|
|
190
|
+
margin: 0 auto
|
|
191
|
+
padding: 40px 20px
|
|
192
|
+
text-align: center
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.title {
|
|
196
|
+
color: #646cff
|
|
197
|
+
font-size: 2.5em
|
|
198
|
+
margin-bottom: 30px
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.counter {
|
|
202
|
+
background: #f5f5f5
|
|
203
|
+
padding: 30px
|
|
204
|
+
border-radius: 12px
|
|
205
|
+
margin-bottom: 20px
|
|
206
|
+
|
|
207
|
+
p {
|
|
208
|
+
font-size: 1.5em
|
|
209
|
+
margin-bottom: 20px
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.buttons {
|
|
214
|
+
display: flex
|
|
215
|
+
gap: 10px
|
|
216
|
+
justify-content: center
|
|
217
|
+
|
|
218
|
+
button {
|
|
219
|
+
font-size: 1.2em
|
|
220
|
+
padding: 10px 24px
|
|
221
|
+
border: none
|
|
222
|
+
border-radius: 8px
|
|
223
|
+
background: #646cff
|
|
224
|
+
color: white
|
|
225
|
+
cursor: pointer
|
|
226
|
+
transition: background 0.2s
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
button:hover {
|
|
230
|
+
background: #535bf2
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.info {
|
|
235
|
+
color: #888
|
|
236
|
+
font-size: 0.9em
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
|
|
241
|
+
writeFileSync(join(projectPath, 'src', 'App.pulse'), appPulse);
|
|
242
|
+
|
|
243
|
+
// Create .gitignore
|
|
244
|
+
const gitignore = `node_modules
|
|
245
|
+
dist
|
|
246
|
+
.DS_Store
|
|
247
|
+
*.local
|
|
248
|
+
`;
|
|
249
|
+
|
|
250
|
+
writeFileSync(join(projectPath, '.gitignore'), gitignore);
|
|
251
|
+
|
|
252
|
+
console.log(`
|
|
253
|
+
Project created successfully!
|
|
254
|
+
|
|
255
|
+
Next steps:
|
|
256
|
+
cd ${projectName}
|
|
257
|
+
npm install
|
|
258
|
+
npm run dev
|
|
259
|
+
|
|
260
|
+
Happy coding with Pulse!
|
|
261
|
+
`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Run development server
|
|
266
|
+
*/
|
|
267
|
+
async function runDev(args) {
|
|
268
|
+
console.log('Starting Pulse development server...');
|
|
269
|
+
|
|
270
|
+
// Use dynamic import for the dev server module
|
|
271
|
+
const { startDevServer } = await import('./dev.js');
|
|
272
|
+
await startDevServer(args);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Build for production
|
|
277
|
+
*/
|
|
278
|
+
async function runBuild(args) {
|
|
279
|
+
console.log('Building Pulse project for production...');
|
|
280
|
+
|
|
281
|
+
const { buildProject } = await import('./build.js');
|
|
282
|
+
await buildProject(args);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Compile a single .pulse file
|
|
287
|
+
*/
|
|
288
|
+
async function compileFile(args) {
|
|
289
|
+
const inputFile = args[0];
|
|
290
|
+
|
|
291
|
+
if (!inputFile) {
|
|
292
|
+
console.error('Please provide a file to compile.');
|
|
293
|
+
console.log('Usage: pulse compile <file.pulse>');
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (!existsSync(inputFile)) {
|
|
298
|
+
console.error(`File not found: ${inputFile}`);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const { compile } = await import('../compiler/index.js');
|
|
303
|
+
|
|
304
|
+
const source = readFileSync(inputFile, 'utf-8');
|
|
305
|
+
const result = compile(source);
|
|
306
|
+
|
|
307
|
+
if (result.success) {
|
|
308
|
+
const outputFile = inputFile.replace(/\.pulse$/, '.js');
|
|
309
|
+
writeFileSync(outputFile, result.code);
|
|
310
|
+
console.log(`Compiled: ${inputFile} -> ${outputFile}`);
|
|
311
|
+
} else {
|
|
312
|
+
console.error('Compilation failed:');
|
|
313
|
+
for (const error of result.errors) {
|
|
314
|
+
console.error(` ${error.message}`);
|
|
315
|
+
}
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Run main
|
|
321
|
+
main().catch(error => {
|
|
322
|
+
console.error('Error:', error.message);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Compiler - Main entry point
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './lexer.js';
|
|
6
|
+
export * from './parser.js';
|
|
7
|
+
export * from './transformer.js';
|
|
8
|
+
|
|
9
|
+
import { tokenize } from './lexer.js';
|
|
10
|
+
import { parse } from './parser.js';
|
|
11
|
+
import { transform } from './transformer.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Compile Pulse source code to JavaScript
|
|
15
|
+
*/
|
|
16
|
+
export function compile(source, options = {}) {
|
|
17
|
+
try {
|
|
18
|
+
// Parse source to AST
|
|
19
|
+
const ast = parse(source);
|
|
20
|
+
|
|
21
|
+
// Transform AST to JavaScript
|
|
22
|
+
const code = transform(ast, options);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
success: true,
|
|
26
|
+
code,
|
|
27
|
+
ast,
|
|
28
|
+
errors: []
|
|
29
|
+
};
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
code: null,
|
|
34
|
+
ast: null,
|
|
35
|
+
errors: [{
|
|
36
|
+
message: error.message,
|
|
37
|
+
line: error.line,
|
|
38
|
+
column: error.column
|
|
39
|
+
}]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse source to AST only
|
|
46
|
+
*/
|
|
47
|
+
export function parseOnly(source) {
|
|
48
|
+
return parse(source);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Tokenize source only
|
|
53
|
+
*/
|
|
54
|
+
export function tokenizeOnly(source) {
|
|
55
|
+
return tokenize(source);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default {
|
|
59
|
+
compile,
|
|
60
|
+
parseOnly,
|
|
61
|
+
tokenizeOnly,
|
|
62
|
+
tokenize,
|
|
63
|
+
parse,
|
|
64
|
+
transform
|
|
65
|
+
};
|