@makano/rew 1.2.53 → 1.2.55

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,9 +2,10 @@ const execOptions = {
2
2
  sharedContext: true,
3
3
  resolveExtensions: ['.coffee', { ext: '.js', options: { type: 'js' } }, { ext: '.qrew', options: { qrew: true } }],
4
4
  nativeRequire: false,
5
+ useImport: false,
5
6
  cwdAlias: '$',
6
7
  jsxPragma: 'createElement',
7
- jsx: false,
8
+ jsx: false
8
9
  };
9
10
 
10
11
  module.exports.execOptions = execOptions;
@@ -10,7 +10,7 @@ function exportsThe(item, name, context) {
10
10
 
11
11
  module.exports.pubFunction = function (context) {
12
12
  return function (name, item) {
13
- if (name && !item) {
13
+ if (name && item == null) {
14
14
  item = name;
15
15
  name = null;
16
16
  }
@@ -8,7 +8,9 @@ const jsYaml = require('js-yaml');
8
8
  const { execOptions } = require('../const/opt');
9
9
 
10
10
  const cachedFiles = [];
11
-
11
+ module.exports.cleanCache = () => {
12
+ while(cachedFiles.length) cachedFiles.pop();
13
+ };
12
14
  const lookUpInOtherApps = (fullPath) => {
13
15
  const con = conf({});
14
16
  const name = fullPath.indexOf('/') ? fullPath.split('/')[0] : fullPath;
@@ -87,7 +89,7 @@ module.exports.imp = function (runPath, context) {
87
89
  ).context.module.exports;
88
90
 
89
91
  if (ispkg) {
90
- const pkg = getPackage(filename)(context);
92
+ const pkg = getPackage(filename)(context, options);
91
93
  exports = pkg._onImport ? pkg._onImport() : pkg;
92
94
  } else if (foundCache) {
93
95
  } else if (type == 'coffee') {
@@ -1,10 +1,26 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { execOptions } = require('../const/opt');
4
+ const { wait } = require('./wait');
5
+
6
+ const cahcedRequires = {};
7
+
8
+ const doImp = (path) => wait(async () => await import(resolvedPath));
3
9
 
4
10
  module.exports.customRequire = function customRequire(modulePath, filePath) {
11
+ const pathname = modulePath;
12
+ if (modulePath.startsWith('./') || modulePath.startsWith('../') || path.isAbsolute(modulePath)) {
13
+ pathname = path.resolve(modulePath);
14
+ }
15
+ if(cahcedRequires[pathname]) {
16
+ return cahcedRequires[pathname];
17
+ }
5
18
  const resolvedPath = resolveModulePath(modulePath, filePath);
6
19
  if(!resolvedPath) throw new Error('Module '+modulePath+' not found');
7
- return require(resolvedPath);
20
+ const r = cahcedRequires[resolvedPath] ? cahcedRequires[resolvedPath] : execOptions.useImport ? doImp(resolvedPath) : require(resolvedPath);
21
+ if(!cahcedRequires[resolvedPath]) cahcedRequires[resolvedPath] = r;
22
+ if(!cahcedRequires[pathname]) cahcedRequires[pathname] = r;
23
+ return r;
8
24
  };
9
25
 
10
26
  function resolveModulePath(modulePath, filePath) {
@@ -115,18 +115,22 @@ function compileRewStuff(content, options) {
115
115
  const token = tokens[i];
116
116
  let { token: nextToken, n } = gnextToken(i, 1, tokens) || {};
117
117
 
118
- if (token.type === 'IDENTIFIER' && token.value === 'opt') {
118
+
119
+ if (token.type === 'IDENTIFIER' && token.value === 'opt.set') {
119
120
  const { token: nextNextToken } = gnextToken(i, 2, tokens) || {};
120
- if (nextNextToken && nextNextToken.value == 'jsxPragma') {
121
+ if (nextNextToken && nextNextToken.value.slice(1).slice(0, -1) == 'jsxPragma') {
121
122
  const { token: nextLastToken } = gnextToken(i, 5, tokens) || {};
122
123
  execOptions.jsxPragma = nextLastToken.value.slice(1).slice(0, -1);
123
124
  }
124
125
  }
125
126
 
126
- if (token.type === 'COMMENT' && token.value.slice(1).trim() === '@jsx') {
127
+ if (token.type === 'COMMENT' && token.value.slice(1).trim().startsWith('@jsx')) {
127
128
  options.jsx = true;
129
+ if(token.value.split('@jsx')[1].trim()){
130
+ options.jsxPragma = token.value.split('@jsx')[1].trim();
131
+ }
128
132
  }
129
-
133
+
130
134
  if (token.type === 'COMMENT' && token.value.slice(1).trim() === '@cls') {
131
135
  options.cls = true;
132
136
  }
@@ -146,7 +150,7 @@ function compileRewStuff(content, options) {
146
150
  }
147
151
 
148
152
 
149
- if (token.type === 'IDENTIFIER' && token.value === 'let') {
153
+ if (token.type === 'IDENTIFIER' && token.value === 'let' && !options.keepImports) {
150
154
  result += '`'
151
155
  hooks.push({
152
156
  index: fnextToken(i, tokens, 'OTHER', ';').ti,
@@ -154,11 +158,11 @@ function compileRewStuff(content, options) {
154
158
  });
155
159
  }
156
160
 
157
- if (token.type === 'IDENTIFIER' && token.value === 'export') {
161
+ if (token.type === 'IDENTIFIER' && token.value === 'export' && !options.keepImports) {
158
162
  token.value = 'pub';
159
163
  }
160
164
 
161
- if (token.type === 'IDENTIFIER' && token.value === 'import') {
165
+ if (token.type === 'IDENTIFIER' && token.value === 'import' && !options.keepImports) {
162
166
  // console.log(nextToken.type);
163
167
  let ind = i + n + 2;
164
168
 
@@ -224,7 +228,7 @@ function compileRewStuff(content, options) {
224
228
  nextToken &&
225
229
  nextToken.type === 'IDENTIFIER' &&
226
230
  nextToken.value &&
227
- nextToken.value !== 'undefined'
231
+ nextToken.value !== 'undefined' && !options.keepImports
228
232
  ) {
229
233
  let next = {...nextToken};
230
234
  if(next.value == 'default'){
@@ -259,13 +263,13 @@ const cpl = (module.exports.compile = function (file, options = {}) {
259
263
  let c = options.type == 'js' || options.compile == false ? file.content : compile(compileRewStuff(file.content, options), {
260
264
  ...options,
261
265
  filename: file.path,
262
- bare: false,
266
+ bare: options.bare ?? false,
263
267
  inlineMap: false,
264
- });
268
+ });2
265
269
  // console.log(c);
266
270
  if (execOptions.jsx || options.jsx) {
267
271
  c = babel.transformSync(c, {
268
- presets: [[babelReact, { pragma: execOptions.jsxPragma }]],
272
+ presets: [[babelReact, { pragma: options.jsxPragma || execOptions.jsxPragma }]],
269
273
  plugins: [],
270
274
  }).code;
271
275
  }
@@ -273,7 +277,8 @@ const cpl = (module.exports.compile = function (file, options = {}) {
273
277
  });
274
278
 
275
279
  module.exports.compileFile = function (filepath, options = {}) {
276
- const f = getFile(filepath);
280
+ const f = typeof filepath == "object" ? filepath : getFile(filepath);
281
+ if(typeof filepath == "object") filepath = filepath.path;
277
282
  let qrew = false;
278
283
 
279
284
  if(options.qrew || path.extname(filepath) == '.qrew') {
@@ -25,7 +25,7 @@ module.exports.prepareContext = function (
25
25
  imports: [],
26
26
  },
27
27
  imports: {
28
- meta: {},
28
+ meta: { url: new URL('file://'+filepath), main: isMainFile(filepath) },
29
29
  assert: options.import ?? {},
30
30
  },
31
31
  app: findAppInfo(filepath),
@@ -1,5 +1,5 @@
1
1
  const vm = require('vm');
2
- const { compileFile } = require('./compiler');
2
+ const { compileFile, compile } = require('./compiler');
3
3
  const { prepareContext } = require('./context');
4
4
  const { existsSync, readFileSync } = require('fs');
5
5
  const { CONFIG_PATH } = require('../const/config_path');
@@ -7,37 +7,36 @@ const path = require('path');
7
7
 
8
8
  const preScript = readFileSync(path.join(__dirname, '../const/pre-exec.js'), { encoding: 'utf-8' });
9
9
 
10
- const exec = (module.exports.exec = function (code, context) {
11
- return vm.runInNewContext(code, vm.createContext(context), {
10
+ const exec = (module.exports.exec = function (code, context, original = '') {
11
+ return vm.runInNewContext(code, context.do ? null : vm.createContext(context), {
12
12
  filename: context.module.filepath,
13
- lineOffset: -1 - preScript.split('\n').length,
13
+ lineOffset: (original.split('\n').length + preScript.split('\n').length) - code.split('\n').length,
14
14
  displayErrors: true,
15
15
  });
16
16
  });
17
17
 
18
18
  module.exports.runPath = function runPath(filepath, options = {}, custom_context = {}) {
19
19
  if(filepath.endsWith('.js')) options.type = 'js';
20
- let { compiled_code, file } = compileFile(filepath, options);
21
- const context = prepareContext(custom_context, options, file.path, runPath);
22
-
23
- context.module.compiled = compiled_code;
24
- context.process.exit = (int) => process.exit(int);
20
+ let { compiled_code, file } = compileFile(options.code ? { content: options.code, path: filepath } : filepath, options);
21
+ const context = options.import?.takeThisContext ? custom_context : prepareContext(custom_context, options, file.path, runPath);
22
+ // context.module.compiled = compiled_code;
23
+ // context.process.exit = (int) => process.exit(int);
25
24
 
26
25
  if(context.app){
27
26
  const p = path.join(CONFIG_PATH, context.app.config.manifest.package, 'app');
28
27
  if(existsSync(p) && context.app.path !== p){
29
28
  console.log("App with the same package name has been installed. Conflicts happened. \nTo fix this, change your app's package name or remove the app making the conflict.");
30
29
  return {
31
- context: { exports: null },
30
+ context: { module: { exports: null } },
32
31
  returns: null
33
32
  }
34
33
  }
35
34
  }
36
35
 
37
- compiled_code = preScript+compiled_code;
36
+ compiled_code = preScript+'\n'+compiled_code;
38
37
 
39
38
  return {
40
39
  context,
41
- returns: exec(compiled_code, context),
40
+ returns: exec(compiled_code, context, file.content),
42
41
  };
43
42
  };
@@ -0,0 +1,370 @@
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
+ module.exports = (context) => {
9
+
10
+ // http.createServer((req, res) => {
11
+ // res.end();
12
+ // }).listen(1400);
13
+
14
+ const imp = (file) => context.imp(file);
15
+ const Web = imp('web');
16
+
17
+ function mkReq(req) {
18
+ const url = `http://${req.headers.host}${req.url}`;
19
+ const options = {
20
+ method: req.method,
21
+ headers: req.headers,
22
+ body: req.body
23
+ };
24
+
25
+ return new Request(url, options);
26
+ }
27
+
28
+ class Server {
29
+ _server = {};
30
+ routers = {};
31
+
32
+ constructor(options){
33
+ this.options = options;
34
+ this._server = http.createServer((req, res) => {
35
+ options.handler ? options.handler(req, res) : this.handleRequest(req, res);
36
+ });
37
+ if(options.routers){
38
+ options.routers.forEach(router => router.to(this));
39
+ }
40
+ }
41
+
42
+ async handleRequest(req, res){
43
+ try {
44
+ let response = new Response();
45
+ const request = mkReq(req);
46
+ if(this.options.fetch == 'router'){
47
+ if(!Object.keys(this.options.routers).length) throw new Error('No fetch function nor routers found');
48
+ response = await this.options.routers[Object.keys(this.options.routers)[0]].fetch(request);
49
+ } else {
50
+ response = await this.options.fetch(request);
51
+ }
52
+
53
+ if(!response){
54
+ res.end('Cannot '+req.method+' '+req.url);
55
+ return;
56
+ }
57
+
58
+ response.headers.forEach((value, name) => {
59
+ res.setHeader(name, value);
60
+ });
61
+
62
+ res.writeHead(response.status);
63
+
64
+ const buffer = await response.arrayBuffer();
65
+ res.end(Buffer.from(buffer));
66
+ } catch (error) {
67
+ // Handle errors
68
+ console.error("Error:", error);
69
+ res.writeHead(500, {'Content-Type': 'text/plain'});
70
+ res.end("Internal Server Error");
71
+ }
72
+ }
73
+
74
+ get listen(){
75
+ this._server.listen(this.options.port);
76
+ return this;
77
+ }
78
+
79
+ set listen(port){
80
+ this.options.port = port;
81
+ return this;
82
+ }
83
+
84
+ port(port){
85
+ this.listen = port;
86
+ return this;
87
+ }
88
+
89
+ log(string){
90
+ console.log(string.replace(/\$([A-Za-z0-9_]+)/g, (_, name) => this.options[name] || _));
91
+ return this;
92
+ }
93
+ }
94
+
95
+ class SvrRouter {
96
+ static new(Class, options, props){
97
+ const router = Class(options);
98
+ for(let i in props) router[i] = props[i];
99
+ router.to = (server) => {
100
+ if(server instanceof Server){
101
+ server.routers[this.id] = this;
102
+ }
103
+ };
104
+ return router;
105
+ }
106
+ }
107
+
108
+ function findLayoutFiles(filePath, root, isClientSide = true, resolveExtensions = ['.coffee', '.js', '.jsx']) {
109
+ const layouts = [];
110
+ const rootDir = root;
111
+ let currentDir = path.dirname(filePath);
112
+
113
+ while (currentDir !== rootDir) {
114
+ for (const ext of resolveExtensions) {
115
+ const layoutFile = path.join(currentDir, `layout${isClientSide ? '' : '.s'}${ext}`);
116
+ if (existsSync(layoutFile)) {
117
+ layouts.push(layoutFile);
118
+ }
119
+ }
120
+ currentDir = path.dirname(currentDir);
121
+ }
122
+
123
+ for (const ext of resolveExtensions) {
124
+ const layoutFile = path.join(currentDir, `layout${isClientSide ? '' : '.s'}${ext}`);
125
+ if (existsSync(layoutFile)) {
126
+ layouts.push(layoutFile);
127
+ }
128
+ }
129
+
130
+ return layouts.reverse();
131
+ }
132
+
133
+ const defaultBundlerEntry = (file, layouts, data) => `
134
+ import * as target from "${file}";
135
+ ${layouts.map((layout, ind) => `import * as layout${ind} from "${layout}";`).join('\n')}
136
+ let page = target.render ? target.render(${JSON.stringify(data)}) : target.default ? target.default(${JSON.stringify(data)}) : null;
137
+ ${layouts.reverse().map((_, ind) => `if (layout${ind}.render) page = layout${ind}.render(${JSON.stringify(data)}, page);`).join('\n')}
138
+ `;
139
+
140
+ const defaultSsrBundlerEntry = (file, layouts, data) => `
141
+ files = "${layouts.join(',')},${file}".split(',')
142
+
143
+ renderers = []
144
+ staticRendering = false
145
+
146
+ for key, file of files
147
+ renderers.push imp file
148
+
149
+ staticRendering = true if renderers[renderers.length-1].staticRendering
150
+
151
+ render = (req, data) ->
152
+ target = renderers.pop()
153
+ page = target.render req, data
154
+ for renderer in renderers
155
+ page = renderer.render req, data, page
156
+ page
157
+
158
+ exports { render, staticRendering }
159
+ `;
160
+
161
+ function createFileRouter({
162
+ onError = () => error(404, 'Path not found'),
163
+ root = '',
164
+ basePath = '',
165
+ resolveExtensions = ['.coffee', '.js', '.jsx'],
166
+ bundlerOptions = {},
167
+ bundlerEntry = defaultBundlerEntry,
168
+ ssrBundlerEntry = defaultSsrBundlerEntry,
169
+ }) {
170
+
171
+ const lookUpFiles = ['route', 'page', 'page.s'];
172
+
173
+ const params = {};
174
+
175
+ function lookUp(pathname) {
176
+ const routeParts = pathname.split('/').filter(Boolean);
177
+ let routePath = root;
178
+
179
+ Object.keys(params).forEach(key => delete params[key]);
180
+
181
+ for (const part of routeParts) {
182
+ const dir = readdirSync(routePath);
183
+
184
+ const match = dir.find(d => d === part || d.match(/^\[.*\]$/));
185
+ if (!match) return null;
186
+
187
+ if (match.match(/^\[.*\]$/)) {
188
+ const paramName = match.slice(1, -1);
189
+ params[paramName] = part;
190
+ }
191
+
192
+ routePath = path.join(routePath, match);
193
+ }
194
+
195
+ for (const base of lookUpFiles) {
196
+ for (const ext of resolveExtensions) {
197
+ const filePath = path.join(routePath, `${base}${ext}`);
198
+ if (existsSync(filePath)) {
199
+ return filePath;
200
+ }
201
+ }
202
+ }
203
+
204
+ return null;
205
+ }
206
+
207
+ function getReqProps(req) {
208
+ return {
209
+ params: {
210
+ ...params,
211
+ ...(req.params || {})
212
+ },
213
+ query: req.query,
214
+ url: req.url,
215
+ method: req.method
216
+ }
217
+ }
218
+
219
+ const w = new Web();
220
+
221
+ async function renderPage(file, basename, req){
222
+ const page = w.create({ viewportMeta: true });
223
+ let staticRendering = false;
224
+ if(basename.endsWith('.s')){
225
+ // SSR is enabled, do only ssr
226
+ const layouts = findLayoutFiles(file, root, false);
227
+ const fileContext = runPath(file, { code: ssrBundlerEntry(file, layouts) }).context.module.exports || {};
228
+ if(typeof fileContext.render !== "function") throw new ReferenceError("Route does not export function render");
229
+ let pageContent = fileContext.render(req, { page, ...getReqProps(req) });
230
+ if(fileContext.staticRendering) staticRendering = true;
231
+ if(!w.isNode(pageContent)) throw new TypeError("Route.render does not return an element");
232
+ if(pageContent?.type?.element == 'head'){
233
+ page.root.props.children.splice(page.root.props.children.indexOf(page.head), 1);
234
+ page.head = pageContent;
235
+ page.root.add(pageContent);
236
+ } else if(pageContent?.type?.element == 'body'){
237
+ page.root.props.children.splice(page.root.props.children.indexOf(page.body), 1);
238
+ page.body = pageContent;
239
+ page.root.add(pageContent);
240
+ } else if(pageContent?.type?.element == 'html'){
241
+ page.root = pageContent;
242
+ page.body = pageContent.find('body');
243
+ page.head = pageContent.find('head');
244
+ } else {
245
+ page.add(pageContent);
246
+ }
247
+ } else {
248
+ const layouts = findLayoutFiles(file, root, true);
249
+ const scriptString = await w.bundle(path.join(root, 'bundle.js'), {
250
+ ...bundlerOptions,
251
+ code: bundlerEntry(file, layouts, getReqProps(req))
252
+ });
253
+ page.script(scriptString);
254
+ staticRendering = true;
255
+ }
256
+ return html(page.render(staticRendering));
257
+ }
258
+
259
+ async function handleRequest(req, file) {
260
+ const ext = path.extname(file);
261
+ const basename = path.basename(file, ext);
262
+
263
+ if (basename.startsWith('route')) {
264
+ const fileContext = run(file).context;
265
+ const handlers = fileContext.module.exports;
266
+ const method = req.method.toUpperCase();
267
+ if (handlers[method]) {
268
+ return await handlers[method](req, getReqProps(req));
269
+ } else {
270
+ return error(405, `Method ${method} not allowed`);
271
+ }
272
+ } else if (basename.startsWith('page')) {
273
+ return await renderPage(file, basename, req);
274
+ }
275
+ }
276
+
277
+
278
+ return async (req) => {
279
+ const url = new URL(req.url);
280
+ const pathname = basePath ? url.pathname.replace(new RegExp('^'+basePath), '') : url.pathname;
281
+ const file = lookUp(pathname);
282
+ cleanCache();
283
+
284
+ if (file) {
285
+ const response = handleRequest(req, file);
286
+ response.catch(() => onError());
287
+ return await response;
288
+ } else {
289
+ return onError();
290
+ }
291
+ };
292
+ }
293
+
294
+
295
+
296
+ class Svr {
297
+ create(options){
298
+ return new Server(options);
299
+ }
300
+
301
+ router({ id = '/', type = 'normal', ...options }){
302
+ let router;
303
+ if(type == 'default') router = SvrRouter.new(IttyRouter, {...options}, { id });
304
+ if(type == 'auto') router = SvrRouter.new(AutoRouter, {...options}, { id });
305
+ if(type == 'normal') router = SvrRouter.new(Router, {...options}, { id });
306
+
307
+ return router;
308
+ }
309
+
310
+ createResponse(format, transform, type = 'normal'){
311
+ return type == 'json' ? json(format, transform) : createResponse(format, transform);
312
+ }
313
+
314
+ html(string, options = {}){
315
+ return html(string, options);
316
+ }
317
+
318
+ json(object, options = {}){
319
+ return json(object, options);
320
+ }
321
+
322
+ jpeg(image, options = {}){
323
+ return jpeg(image, options);
324
+ }
325
+
326
+ png(image, options = {}){
327
+ return png(image, options);
328
+ }
329
+
330
+ webp(image, options = {}){
331
+ return webp(image, options);
332
+ }
333
+
334
+ text(string, options = {}){
335
+ return text(string, options);
336
+ }
337
+
338
+ status(code, options = {}){
339
+ return status(code, options);
340
+ }
341
+
342
+ cors(options = {}){
343
+ return cors(options);
344
+ }
345
+
346
+ error(status, body){
347
+ return error(status, body);
348
+ }
349
+
350
+ createFileRouter(o){
351
+ return createFileRouter(o);
352
+ }
353
+ }
354
+
355
+ class SvrResponse extends Response {}
356
+ class SvrRequest extends Request {}
357
+
358
+ Svr.prototype.Response = SvrResponse;
359
+ Svr.prototype.Request = SvrRequest;
360
+ Svr.prototype.URL = URL;
361
+ Svr.prototype.StatusError = StatusError;
362
+
363
+ Svr.prototype.withContent = withContent;
364
+ Svr.prototype.withCookies = withCookies;
365
+ Svr.prototype.withParams = withParams;
366
+
367
+ IttyRouter
368
+
369
+ return Svr;
370
+ }
@@ -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
+ })