@makano/rew 1.2.5 → 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/lib/civet/main.js +17239 -0
  2. package/lib/rew/cli/cli.js +34 -5
  3. package/lib/rew/cli/utils.js +43 -12
  4. package/lib/rew/const/default.js +2 -1
  5. package/lib/rew/const/ext.js +5 -0
  6. package/lib/rew/const/opt.js +7 -2
  7. package/lib/rew/const/usage.js +15 -0
  8. package/lib/rew/functions/export.js +1 -1
  9. package/lib/rew/functions/fs.js +6 -1
  10. package/lib/rew/functions/import.js +17 -12
  11. package/lib/rew/functions/require.js +17 -1
  12. package/lib/rew/functions/stdout.js +4 -0
  13. package/lib/rew/main.js +1 -13
  14. package/lib/rew/modules/compiler.js +103 -34
  15. package/lib/rew/modules/context.js +13 -1
  16. package/lib/rew/modules/runtime.js +37 -20
  17. package/lib/rew/pkgs/serve.js +373 -0
  18. package/lib/rew/pkgs/stream.js +10 -0
  19. package/lib/rew/pkgs/web.js +504 -0
  20. package/package.json +7 -5
  21. package/runtime.d.ts +718 -146
  22. package/jsconfig.json +0 -13
  23. package/lib/coffeescript/browser.js +0 -156
  24. package/lib/coffeescript/cake.js +0 -134
  25. package/lib/coffeescript/coffeescript.js +0 -465
  26. package/lib/coffeescript/command.js +0 -832
  27. package/lib/coffeescript/grammar.js +0 -1930
  28. package/lib/coffeescript/helpers.js +0 -513
  29. package/lib/coffeescript/index.js +0 -230
  30. package/lib/coffeescript/lexer.js +0 -2316
  31. package/lib/coffeescript/nodes.js +0 -9784
  32. package/lib/coffeescript/optparse.js +0 -258
  33. package/lib/coffeescript/parser.js +0 -20384
  34. package/lib/coffeescript/register.js +0 -120
  35. package/lib/coffeescript/repl.js +0 -328
  36. package/lib/coffeescript/rewriter.js +0 -1448
  37. package/lib/coffeescript/scope.js +0 -191
  38. package/lib/coffeescript/sourcemap.js +0 -244
@@ -0,0 +1,373 @@
1
+ const { readdirSync, existsSync } = require('fs');
2
+ const http = require('http');
3
+ const { IttyRouter, AutoRouter, Router, createResponse, cors, error, StatusError, html, json, withCookies, withContent, withParams, jpeg, png, webp, text, status } = require('itty-router');
4
+ const path = require('path');
5
+ const { run } = require('../main');
6
+ const { runPath } = require('../modules/runtime');
7
+ const { cleanCache } = require('../functions/import');
8
+ const { REW_FILE_TYPE } = require('../const/ext');
9
+
10
+ const lookUpFiles = ['route', 'page', 'page.s'];
11
+
12
+ module.exports = (context) => {
13
+
14
+ // http.createServer((req, res) => {
15
+ // res.end();
16
+ // }).listen(1400);
17
+
18
+ const imp = (file) => context.imp(file);
19
+ const Web = imp('web');
20
+
21
+ function mkReq(req) {
22
+ const url = `http://${req.headers.host}${req.url}`;
23
+ const options = {
24
+ method: req.method,
25
+ headers: req.headers,
26
+ body: req.body
27
+ };
28
+
29
+ return new Request(url, options);
30
+ }
31
+
32
+ class Server {
33
+ _server = {};
34
+ routers = {};
35
+
36
+ constructor(options){
37
+ this.options = options;
38
+ this._server = http.createServer((req, res) => {
39
+ options.handler ? options.handler(req, res) : this.handleRequest(req, res);
40
+ });
41
+ if(options.routers){
42
+ options.routers.forEach(router => router.to(this));
43
+ }
44
+ }
45
+
46
+ async handleRequest(req, res){
47
+ try {
48
+ let response = new Response();
49
+ const request = mkReq(req);
50
+ if(this.options.fetch == 'router'){
51
+ if(!Object.keys(this.options.routers).length) throw new Error('No fetch function nor routers found');
52
+ response = await this.options.routers[Object.keys(this.options.routers)[0]].fetch(request);
53
+ } else {
54
+ response = await this.options.fetch(request);
55
+ }
56
+
57
+ if(!response){
58
+ res.end('Cannot '+req.method+' '+req.url);
59
+ return;
60
+ }
61
+
62
+ response.headers.forEach((value, name) => {
63
+ res.setHeader(name, value);
64
+ });
65
+
66
+ res.writeHead(response.status);
67
+
68
+ const buffer = await response.arrayBuffer();
69
+ res.end(Buffer.from(buffer));
70
+ } catch (error) {
71
+ // Handle errors
72
+ console.error("Error:", error);
73
+ res.writeHead(500, {'Content-Type': 'text/plain'});
74
+ res.end("Internal Server Error");
75
+ }
76
+ }
77
+
78
+ get listen(){
79
+ this._server.listen(this.options.port);
80
+ return this;
81
+ }
82
+
83
+ set listen(port){
84
+ this.options.port = port;
85
+ return this;
86
+ }
87
+
88
+ port(port){
89
+ this.listen = port;
90
+ return this;
91
+ }
92
+
93
+ log(string){
94
+ console.log(string.replace(/\$([A-Za-z0-9_]+)/g, (_, name) => this.options[name] || _));
95
+ return this;
96
+ }
97
+ }
98
+
99
+ class SvrRouter {
100
+ static new(Class, options, props){
101
+ const router = Class(options);
102
+ for(let i in props) router[i] = props[i];
103
+ router.to = (server) => {
104
+ if(server instanceof Server){
105
+ server.routers[this.id] = this;
106
+ }
107
+ };
108
+ return router;
109
+ }
110
+ }
111
+
112
+ function findLayoutFiles(filePath, root, isClientSide = true, resolveExtensions = [REW_FILE_TYPE.EXTENSION, '.coffee', '.js', '.jsx']) {
113
+ const layouts = [];
114
+ const rootDir = root;
115
+ let currentDir = path.dirname(filePath);
116
+
117
+ while (currentDir !== rootDir) {
118
+ for (const ext of resolveExtensions) {
119
+ const layoutFile = path.join(currentDir, `layout${isClientSide ? '' : '.s'}${ext}`);
120
+ if (existsSync(layoutFile)) {
121
+ layouts.push(layoutFile);
122
+ }
123
+ }
124
+ currentDir = path.dirname(currentDir);
125
+ }
126
+
127
+ for (const ext of resolveExtensions) {
128
+ const layoutFile = path.join(currentDir, `layout${isClientSide ? '' : '.s'}${ext}`);
129
+ if (existsSync(layoutFile)) {
130
+ layouts.push(layoutFile);
131
+ }
132
+ }
133
+
134
+ return layouts.reverse();
135
+ }
136
+
137
+ const defaultBundlerEntry = (file, layouts, data) => `
138
+ import * as target from "${file}";
139
+ ${layouts.map((layout, ind) => `import * as layout${ind} from "${layout}";`).join('\n')}
140
+ let page = target.render ? target.render(${JSON.stringify(data)}) : target.default ? target.default(${JSON.stringify(data)}) : null;
141
+ ${layouts.reverse().map((_, ind) => `if (layout${ind}.render) page = layout${ind}.render(${JSON.stringify(data)}, page);`).join('\n')}
142
+ `;
143
+
144
+ const defaultSsrBundlerEntry = (file, layouts, data) => `
145
+ files = "${layouts.length ? layouts.join(',')+',' : ''}${file}".split(',')
146
+
147
+ renderers = []
148
+ staticRendering = false
149
+
150
+ for key, file of files
151
+ renderers.push imp file
152
+
153
+ staticRendering = true if renderers[renderers.length-1].staticRendering
154
+
155
+ render = (req, data) ->
156
+ target = renderers.pop()
157
+ page = target.render req, data
158
+ for renderer in renderers
159
+ page = renderer.render req, data, page
160
+ page
161
+
162
+ exports { render, staticRendering }
163
+ `;
164
+
165
+ function createFileRouter({
166
+ onError = () => error(404, 'Path not found'),
167
+ root = '',
168
+ basePath = '',
169
+ resolveExtensions = [REW_FILE_TYPE.EXTENSION, '.coffee', '.js', '.jsx'],
170
+ bundlerOptions = {},
171
+ bundlerEntry = defaultBundlerEntry,
172
+ ssrBundlerEntry = defaultSsrBundlerEntry,
173
+ }) {
174
+
175
+ const params = {};
176
+
177
+ function lookUp(pathname) {
178
+ const routeParts = pathname.split('/').filter(Boolean);
179
+ let routePath = root;
180
+
181
+
182
+ Object.keys(params).forEach(key => delete params[key]);
183
+
184
+ for (const part of routeParts) {
185
+ const dir = readdirSync(routePath);
186
+
187
+ const match = dir.find(d => d === part || d.match(/^\[.*\]$/));
188
+ if (!match) return null;
189
+
190
+ if (match.match(/^\[.*\]$/)) {
191
+ const paramName = match.slice(1, -1);
192
+ params[paramName] = part;
193
+ }
194
+
195
+ routePath = path.join(routePath, match);
196
+ }
197
+
198
+ for (const base of lookUpFiles) {
199
+ for (const ext of resolveExtensions) {
200
+ const filePath = path.join(routePath, `${base}${ext}`);
201
+ if (existsSync(filePath)) {
202
+ return filePath;
203
+ }
204
+ }
205
+ }
206
+
207
+ return null;
208
+ }
209
+
210
+ function getReqProps(req) {
211
+ return {
212
+ params: {
213
+ ...params,
214
+ ...(req.params || {})
215
+ },
216
+ query: req.query,
217
+ url: req.url,
218
+ method: req.method
219
+ }
220
+ }
221
+
222
+ const w = new Web();
223
+
224
+ async function renderPage(file, basename, req){
225
+ const page = w.create({ viewportMeta: true });
226
+ let staticRendering = false;
227
+ if(basename.endsWith('.s')){
228
+ // SSR is enabled, do only ssr
229
+ const layouts = findLayoutFiles(file, root, false);
230
+ const fileContext = runPath(file, { code: ssrBundlerEntry(file, layouts) }).context.module.exports || {};
231
+ if(typeof fileContext.render !== "function") throw new ReferenceError("Route does not export function render");
232
+ let pageContent = fileContext.render(req, { page, ...getReqProps(req) });
233
+ if(fileContext.staticRendering) staticRendering = true;
234
+ if(!w.isNode(pageContent)) throw new TypeError("Route.render does not return an element");
235
+ if(pageContent?.type?.element == 'head'){
236
+ page.root.props.children.splice(page.root.props.children.indexOf(page.head), 1);
237
+ page.head = pageContent;
238
+ page.root.add(pageContent);
239
+ } else if(pageContent?.type?.element == 'body'){
240
+ page.root.props.children.splice(page.root.props.children.indexOf(page.body), 1);
241
+ page.body = pageContent;
242
+ page.root.add(pageContent);
243
+ } else if(pageContent?.type?.element == 'html'){
244
+ page.root = pageContent;
245
+ page.body = pageContent.find('body');
246
+ page.head = pageContent.find('head');
247
+ } else {
248
+ page.add(pageContent);
249
+ }
250
+ } else {
251
+ const layouts = findLayoutFiles(file, root, true);
252
+ const scriptString = await w.bundle(path.join(root, 'bundle.js'), {
253
+ ...bundlerOptions,
254
+ code: bundlerEntry(file, layouts, getReqProps(req))
255
+ });
256
+ page.script(scriptString);
257
+ staticRendering = true;
258
+ }
259
+ return html(page.render(staticRendering));
260
+ }
261
+
262
+ async function handleRequest(req, file) {
263
+ const ext = path.extname(file);
264
+ const basename = path.basename(file, ext);
265
+
266
+ if (basename.startsWith('route')) {
267
+ const fileContext = run(file).context;
268
+ const handlers = fileContext.module.exports;
269
+ const method = req.method.toUpperCase();
270
+ if (handlers[method]) {
271
+ return await handlers[method](req, getReqProps(req));
272
+ } else {
273
+ return error(405, `Method ${method} not allowed`);
274
+ }
275
+ } else if (basename.startsWith('page')) {
276
+ return await renderPage(file, basename, req);
277
+ }
278
+ }
279
+
280
+
281
+ return async (req) => {
282
+ const url = new URL(req.url);
283
+ const pathname = basePath ? url.pathname.replace(new RegExp('^'+basePath), '') : url.pathname;
284
+ const file = lookUp(pathname);
285
+ cleanCache();
286
+
287
+ if (file) {
288
+ const response = handleRequest(req, file);
289
+ response.catch(() => onError());
290
+ return await response;
291
+ } else {
292
+ return onError();
293
+ }
294
+ };
295
+ }
296
+
297
+
298
+
299
+ class Svr {
300
+ create(options){
301
+ return new Server(options);
302
+ }
303
+
304
+ router({ id = '/', type = 'normal', ...options }){
305
+ let router;
306
+ if(type == 'default') router = SvrRouter.new(IttyRouter, {...options}, { id });
307
+ if(type == 'auto') router = SvrRouter.new(AutoRouter, {...options}, { id });
308
+ if(type == 'normal') router = SvrRouter.new(Router, {...options}, { id });
309
+
310
+ return router;
311
+ }
312
+
313
+ createResponse(format, transform, type = 'normal'){
314
+ return type == 'json' ? json(format, transform) : createResponse(format, transform);
315
+ }
316
+
317
+ html(string, options = {}){
318
+ return html(string, options);
319
+ }
320
+
321
+ json(object, options = {}){
322
+ return json(object, options);
323
+ }
324
+
325
+ jpeg(image, options = {}){
326
+ return jpeg(image, options);
327
+ }
328
+
329
+ png(image, options = {}){
330
+ return png(image, options);
331
+ }
332
+
333
+ webp(image, options = {}){
334
+ return webp(image, options);
335
+ }
336
+
337
+ text(string, options = {}){
338
+ return text(string, options);
339
+ }
340
+
341
+ status(code, options = {}){
342
+ return status(code, options);
343
+ }
344
+
345
+ cors(options = {}){
346
+ return cors(options);
347
+ }
348
+
349
+ error(status, body){
350
+ return error(status, body);
351
+ }
352
+
353
+ createFileRouter(o){
354
+ return createFileRouter(o);
355
+ }
356
+ }
357
+
358
+ class SvrResponse extends Response {}
359
+ class SvrRequest extends Request {}
360
+
361
+ Svr.prototype.Response = SvrResponse;
362
+ Svr.prototype.Request = SvrRequest;
363
+ Svr.prototype.URL = URL;
364
+ Svr.prototype.StatusError = StatusError;
365
+
366
+ Svr.prototype.withContent = withContent;
367
+ Svr.prototype.withCookies = withCookies;
368
+ Svr.prototype.withParams = withParams;
369
+
370
+ IttyRouter
371
+
372
+ return Svr;
373
+ }
@@ -0,0 +1,10 @@
1
+ const { Readable, Writable, Transform, Duplex, pipeline, finished } = require('stream');
2
+
3
+ module.exports = (context) => ({
4
+ Readable,
5
+ Writable,
6
+ Transform,
7
+ Duplex,
8
+ pipeline,
9
+ finished
10
+ })