@makano/rew 1.2.53 → 1.2.54

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