solid-refresh 0.6.3 → 0.7.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.
Files changed (44) hide show
  1. package/README.md +38 -10
  2. package/dist/babel.cjs +580 -283
  3. package/dist/babel.mjs +580 -283
  4. package/dist/types/src/babel/core/checks.d.ts +4 -0
  5. package/dist/types/src/babel/core/checks.d.ts.map +1 -0
  6. package/dist/types/src/babel/core/constants.d.ts +8 -0
  7. package/dist/types/src/babel/core/constants.d.ts.map +1 -0
  8. package/dist/types/src/babel/core/create-registry.d.ts +5 -0
  9. package/dist/types/src/babel/core/create-registry.d.ts.map +1 -0
  10. package/dist/types/src/babel/core/generate-unique-name.d.ts +4 -0
  11. package/dist/types/src/babel/core/generate-unique-name.d.ts.map +1 -0
  12. package/dist/types/src/babel/core/generator.d.ts +3 -0
  13. package/dist/types/src/babel/core/generator.d.ts.map +1 -0
  14. package/dist/types/src/babel/core/get-descriptive-name.d.ts +3 -0
  15. package/dist/types/src/babel/core/get-descriptive-name.d.ts.map +1 -0
  16. package/dist/types/src/babel/core/get-foreign-bindings.d.ts +4 -0
  17. package/dist/types/src/babel/core/get-foreign-bindings.d.ts.map +1 -0
  18. package/dist/types/src/babel/core/get-hmr-decline-call.d.ts +4 -0
  19. package/dist/types/src/babel/core/get-hmr-decline-call.d.ts.map +1 -0
  20. package/dist/types/src/babel/core/get-hot-identifier.d.ts +4 -0
  21. package/dist/types/src/babel/core/get-hot-identifier.d.ts.map +1 -0
  22. package/dist/types/src/babel/core/get-import-identifier.d.ts +5 -0
  23. package/dist/types/src/babel/core/get-import-identifier.d.ts.map +1 -0
  24. package/dist/types/src/babel/core/get-root-statement-path.d.ts +3 -0
  25. package/dist/types/src/babel/core/get-root-statement-path.d.ts.map +1 -0
  26. package/dist/types/src/babel/core/get-statement-path.d.ts +3 -0
  27. package/dist/types/src/babel/core/get-statement-path.d.ts.map +1 -0
  28. package/dist/types/src/babel/core/get-vite-hmr-requirement.d.ts +4 -0
  29. package/dist/types/src/babel/core/get-vite-hmr-requirement.d.ts.map +1 -0
  30. package/dist/types/src/babel/core/is-valid-callee.d.ts +5 -0
  31. package/dist/types/src/babel/core/is-valid-callee.d.ts.map +1 -0
  32. package/dist/types/src/babel/core/register-import-specifiers.d.ts +5 -0
  33. package/dist/types/src/babel/core/register-import-specifiers.d.ts.map +1 -0
  34. package/dist/types/src/babel/core/transform-jsx.d.ts +4 -0
  35. package/dist/types/src/babel/core/transform-jsx.d.ts.map +1 -0
  36. package/dist/types/src/babel/core/types.d.ts +41 -0
  37. package/dist/types/src/babel/core/types.d.ts.map +1 -0
  38. package/dist/types/src/babel/core/unwrap.d.ts +11 -0
  39. package/dist/types/src/babel/core/unwrap.d.ts.map +1 -0
  40. package/dist/types/src/babel/core/xxhash32.d.ts +7 -0
  41. package/dist/types/src/babel/core/xxhash32.d.ts.map +1 -0
  42. package/dist/types/src/babel/index.d.ts +1 -24
  43. package/dist/types/src/babel/index.d.ts.map +1 -1
  44. package/package.json +1 -1
package/dist/babel.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from 'path';
2
2
  import * as t from '@babel/types';
3
+ import { addNamed, addDefault } from '@babel/helper-module-imports';
3
4
  import _generator from '@babel/generator';
4
- import { addNamed } from '@babel/helper-module-imports';
5
5
 
6
6
  /**
7
7
  * Copyright (c) 2019 Jason Dent
@@ -190,79 +190,244 @@ function xxHash32(buffer, seed = 0) {
190
190
  return acc < 0 ? acc + 4294967296 : acc;
191
191
  }
192
192
 
193
- // https://github.com/babel/babel/issues/15269
194
- let generator;
195
- if (typeof _generator !== 'function') {
196
- generator = _generator.default;
197
- }
198
- else {
199
- generator = _generator;
200
- }
201
- const CWD = process.cwd();
202
- function getFile(filename) {
203
- return path.relative(CWD, filename);
204
- }
205
193
  // This is just a Pascal heuristic
206
194
  // we only assume a function is a component
207
195
  // if the first character is in uppercase
208
196
  function isComponentishName(name) {
209
197
  return name[0] >= 'A' && name[0] <= 'Z';
210
198
  }
211
- function isESMHMR(bundler) {
212
- // The currently known ESM HMR implementations
213
- // esm - the original ESM HMR Spec
214
- // vite - Vite's implementation
215
- return bundler === 'esm' || bundler === 'vite';
199
+ function getImportSpecifierName(specifier) {
200
+ if (t.isIdentifier(specifier.imported)) {
201
+ return specifier.imported.name;
202
+ }
203
+ return specifier.imported.value;
216
204
  }
217
- // Source of solid-refresh (for import)
218
- const SOLID_REFRESH_MODULE = 'solid-refresh';
219
- // Exported names from solid-refresh that will be imported
220
- const IMPORTS = {
221
- registry: '$$registry',
222
- refresh: '$$refresh',
223
- component: '$$component',
224
- context: '$$context',
225
- decline: '$$decline',
226
- };
227
- function getSolidRefreshIdentifier(state, path, name) {
228
- const target = `${name}`;
229
- const current = state.hooks.get(target);
230
- if (current) {
231
- return current;
205
+
206
+ function registerImportSpecifier(state, id, specifier) {
207
+ if (t.isImportDefaultSpecifier(specifier)) {
208
+ if (id.definition.kind === 'default') {
209
+ state.registrations.identifiers.set(specifier.local, id);
210
+ }
211
+ return;
212
+ }
213
+ if (t.isImportSpecifier(specifier)) {
214
+ if ((id.definition.kind === 'named' &&
215
+ getImportSpecifierName(specifier) === id.definition.name) ||
216
+ (id.definition.kind === 'default' &&
217
+ getImportSpecifierName(specifier) === 'default')) {
218
+ state.registrations.identifiers.set(specifier.local, id);
219
+ }
220
+ return;
221
+ }
222
+ let current = state.registrations.namespaces.get(specifier.local);
223
+ if (!current) {
224
+ current = [];
225
+ }
226
+ current.push(id);
227
+ state.registrations.namespaces.set(specifier.local, current);
228
+ }
229
+ function registerImportSpecifiers(state, path, definitions) {
230
+ for (let i = 0, len = definitions.length; i < len; i++) {
231
+ const id = definitions[i];
232
+ if (path.node.source.value === id.definition.source) {
233
+ for (let k = 0, klen = path.node.specifiers.length; k < klen; k++) {
234
+ registerImportSpecifier(state, id, path.node.specifiers[k]);
235
+ }
236
+ }
232
237
  }
233
- const newID = addNamed(path, name, SOLID_REFRESH_MODULE);
234
- state.hooks.set(target, newID);
235
- return newID;
236
238
  }
239
+
237
240
  function getHotIdentifier(state) {
238
- const bundler = state.opts.bundler;
239
- // vite/esm uses `import.meta.hot`
240
- if (isESMHMR(bundler)) {
241
- return t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('hot'));
241
+ switch (state.bundler) {
242
+ // vite/esm uses `import.meta.hot`
243
+ case 'esm':
244
+ case 'vite':
245
+ return t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('hot'));
246
+ // webpack 5 uses `import.meta.webpackHot`
247
+ // rspack does as well
248
+ case 'webpack5':
249
+ case 'rspack-esm':
250
+ return t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('webpackHot'));
251
+ default:
252
+ // `module.hot` is the default.
253
+ return t.memberExpression(t.identifier('module'), t.identifier('hot'));
242
254
  }
243
- // webpack 5 uses `import.meta.webpackHot`
244
- // rspack does as well
245
- if (bundler === 'webpack5' || bundler === 'rspack-esm') {
246
- return t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('webpackHot'));
255
+ }
256
+
257
+ function getImportIdentifier(state, path, registration) {
258
+ const name = registration.kind === 'named' ? registration.name : 'default';
259
+ const target = `${registration.source}[${name}]`;
260
+ const current = state.imports.get(target);
261
+ if (current) {
262
+ return current;
247
263
  }
248
- // `module.hot` is the default.
249
- return t.memberExpression(t.identifier('module'), t.identifier('hot'));
264
+ const newID = registration.kind === 'named'
265
+ ? addNamed(path, registration.name, registration.source)
266
+ : addDefault(path, registration.source);
267
+ state.imports.set(target, newID);
268
+ return newID;
250
269
  }
251
- function generateViteRequirement(state, statements, pathToHot) {
252
- if (state.opts.bundler === 'vite') {
270
+
271
+ // Source of solid-refresh (for import)
272
+ const SOLID_REFRESH_MODULE = 'solid-refresh';
273
+ // Exported names from solid-refresh that will be imported
274
+ const IMPORT_REGISTRY = {
275
+ kind: 'named',
276
+ name: '$$registry',
277
+ source: SOLID_REFRESH_MODULE,
278
+ };
279
+ const IMPORT_REFRESH = {
280
+ kind: 'named',
281
+ name: '$$refresh',
282
+ source: SOLID_REFRESH_MODULE,
283
+ };
284
+ const IMPORT_COMPONENT = {
285
+ kind: 'named',
286
+ name: '$$component',
287
+ source: SOLID_REFRESH_MODULE,
288
+ };
289
+ const IMPORT_CONTEXT = {
290
+ kind: 'named',
291
+ name: '$$context',
292
+ source: SOLID_REFRESH_MODULE,
293
+ };
294
+ const IMPORT_DECLINE = {
295
+ kind: 'named',
296
+ name: '$$decline',
297
+ source: SOLID_REFRESH_MODULE,
298
+ };
299
+ const IMPORT_SPECIFIERS = [
300
+ {
301
+ type: 'render',
302
+ definition: { name: 'render', kind: 'named', source: 'solid-js/web' },
303
+ },
304
+ {
305
+ type: 'render',
306
+ definition: { name: 'hydrate', kind: 'named', source: 'solid-js/web' },
307
+ },
308
+ {
309
+ type: 'createContext',
310
+ definition: {
311
+ name: 'createContext',
312
+ kind: 'named',
313
+ source: 'solid-js',
314
+ },
315
+ },
316
+ {
317
+ type: 'createContext',
318
+ definition: {
319
+ name: 'createContext',
320
+ kind: 'named',
321
+ source: 'solid-js/web',
322
+ },
323
+ },
324
+ ];
325
+
326
+ function generateViteHMRRequirement(state, statements, pathToHot) {
327
+ if (state.bundler === 'vite') {
253
328
  // Vite requires that the owner module has an `import.meta.hot.accept()` call
254
329
  statements.push(t.expressionStatement(t.callExpression(t.memberExpression(pathToHot, t.identifier('accept')), [])));
255
330
  }
256
331
  }
332
+
257
333
  function getHMRDeclineCall(state, path) {
258
- var _a;
259
334
  const pathToHot = getHotIdentifier(state);
260
335
  const statements = [
261
- t.expressionStatement(t.callExpression(getSolidRefreshIdentifier(state, path, IMPORTS.decline), [t.stringLiteral((_a = state.opts.bundler) !== null && _a !== void 0 ? _a : 'standard'), pathToHot])),
336
+ t.expressionStatement(t.callExpression(getImportIdentifier(state, path, IMPORT_DECLINE), [
337
+ t.stringLiteral(state.bundler),
338
+ pathToHot,
339
+ ])),
262
340
  ];
263
- generateViteRequirement(state, statements, pathToHot);
341
+ generateViteHMRRequirement(state, statements, pathToHot);
264
342
  return t.ifStatement(pathToHot, t.blockStatement(statements));
265
343
  }
344
+
345
+ function isPathValid(path, key) {
346
+ return key(path.node);
347
+ }
348
+ function isNestedExpression(node) {
349
+ switch (node.type) {
350
+ case 'ParenthesizedExpression':
351
+ case 'TypeCastExpression':
352
+ case 'TSAsExpression':
353
+ case 'TSSatisfiesExpression':
354
+ case 'TSNonNullExpression':
355
+ case 'TSTypeAssertion':
356
+ case 'TSInstantiationExpression':
357
+ return true;
358
+ default:
359
+ return false;
360
+ }
361
+ }
362
+ function unwrapNode(node, key) {
363
+ if (key(node)) {
364
+ return node;
365
+ }
366
+ if (isNestedExpression(node)) {
367
+ return unwrapNode(node.expression, key);
368
+ }
369
+ return undefined;
370
+ }
371
+
372
+ function isIdentifierValidCallee(state, path, callee, target) {
373
+ const binding = path.scope.getBindingIdentifier(callee.name);
374
+ if (binding) {
375
+ const result = state.registrations.identifiers.get(binding);
376
+ if (result && result.type === target) {
377
+ return true;
378
+ }
379
+ }
380
+ return false;
381
+ }
382
+ function isPropertyValidCallee(result, target, propName) {
383
+ for (let i = 0, len = result.length; i < len; i++) {
384
+ const registration = result[i];
385
+ if (registration.type === target) {
386
+ if (registration.definition.kind === 'named') {
387
+ if (registration.definition.name === propName) {
388
+ return true;
389
+ }
390
+ }
391
+ else if (propName === 'default') {
392
+ return true;
393
+ }
394
+ }
395
+ }
396
+ return false;
397
+ }
398
+ function isMemberExpressionValidCallee(state, path, member, target) {
399
+ if (!t.isIdentifier(member.property)) {
400
+ return false;
401
+ }
402
+ const trueObject = unwrapNode(member.object, t.isIdentifier);
403
+ if (!trueObject) {
404
+ return false;
405
+ }
406
+ const binding = path.scope.getBindingIdentifier(trueObject.name);
407
+ if (!binding) {
408
+ return false;
409
+ }
410
+ const result = state.registrations.namespaces.get(binding);
411
+ if (!result) {
412
+ return false;
413
+ }
414
+ return isPropertyValidCallee(result, target, member.property.name);
415
+ }
416
+ function isValidCallee(state, path, { callee }, target) {
417
+ if (t.isV8IntrinsicIdentifier(callee)) {
418
+ return false;
419
+ }
420
+ const trueCallee = unwrapNode(callee, t.isIdentifier);
421
+ if (trueCallee) {
422
+ return isIdentifierValidCallee(state, path, trueCallee, target);
423
+ }
424
+ const trueMember = unwrapNode(callee, t.isMemberExpression);
425
+ if (trueMember && !trueMember.computed) {
426
+ return isMemberExpressionValidCallee(state, path, trueMember, target);
427
+ }
428
+ return false;
429
+ }
430
+
266
431
  function getStatementPath(path) {
267
432
  if (t.isStatement(path.node)) {
268
433
  return path;
@@ -272,40 +437,46 @@ function getStatementPath(path) {
272
437
  }
273
438
  return null;
274
439
  }
440
+
441
+ function getRootStatementPath(path) {
442
+ let current = path.parentPath;
443
+ while (current) {
444
+ const next = current.parentPath;
445
+ if (next && t.isProgram(next.node)) {
446
+ return current;
447
+ }
448
+ current = next;
449
+ }
450
+ return path;
451
+ }
452
+
275
453
  const REGISTRY = 'REGISTRY';
276
454
  function createRegistry(state, path) {
277
- var _a;
278
- const current = state.hooks.get(REGISTRY);
455
+ const current = state.imports.get(REGISTRY);
279
456
  if (current) {
280
457
  return current;
281
458
  }
282
- const program = path.scope.getProgramParent();
283
- const identifier = program.generateUidIdentifier(REGISTRY);
284
- program.push({
285
- id: identifier,
286
- kind: 'const',
287
- init: t.callExpression(getSolidRefreshIdentifier(state, path, IMPORTS.registry), []),
288
- });
459
+ const root = getRootStatementPath(path);
460
+ const identifier = path.scope.generateUidIdentifier(REGISTRY);
461
+ root.insertBefore(t.variableDeclaration('const', [
462
+ t.variableDeclarator(identifier, t.callExpression(getImportIdentifier(state, path, IMPORT_REGISTRY), [])),
463
+ ]));
289
464
  const pathToHot = getHotIdentifier(state);
290
465
  const statements = [
291
- t.expressionStatement(t.callExpression(getSolidRefreshIdentifier(state, path, IMPORTS.refresh), [
292
- t.stringLiteral((_a = state.opts.bundler) !== null && _a !== void 0 ? _a : 'standard'),
466
+ t.expressionStatement(t.callExpression(getImportIdentifier(state, path, IMPORT_REFRESH), [
467
+ t.stringLiteral(state.bundler),
293
468
  pathToHot,
294
469
  identifier,
295
470
  ])),
296
471
  ];
297
- generateViteRequirement(state, statements, pathToHot);
298
- program.path.pushContainer('body', [
472
+ generateViteHMRRequirement(state, statements, pathToHot);
473
+ path.scope.getProgramParent().path.pushContainer('body', [
299
474
  t.ifStatement(pathToHot, t.blockStatement(statements)),
300
475
  ]);
301
- state.hooks.set(REGISTRY, identifier);
476
+ state.imports.set(REGISTRY, identifier);
302
477
  return identifier;
303
478
  }
304
- function createSignatureValue(node) {
305
- const code = generator(node);
306
- const result = xxHash32(code.code).toString(16);
307
- return result;
308
- }
479
+
309
480
  function isForeignBinding(source, current, name) {
310
481
  if (source === current) {
311
482
  return true;
@@ -328,13 +499,15 @@ function isInTypescript(path) {
328
499
  }
329
500
  return false;
330
501
  }
331
- function getBindings(path) {
502
+ function getForeignBindings(path) {
332
503
  const identifiers = new Set();
333
504
  path.traverse({
334
505
  ReferencedIdentifier(p) {
335
506
  // Check identifiers that aren't in a TS expression
336
507
  if (!isInTypescript(p) && isForeignBinding(path, p, p.node.name)) {
337
- identifiers.add(p.node.name);
508
+ if (p.isIdentifier() || p.parentPath.isJSXMemberExpression()) {
509
+ identifiers.add(p.node.name);
510
+ }
338
511
  }
339
512
  },
340
513
  });
@@ -344,149 +517,266 @@ function getBindings(path) {
344
517
  }
345
518
  return collected;
346
519
  }
347
- const IMPORT_IDENTITIES = [
348
- {
349
- type: 'createContext',
350
- name: 'createContext',
351
- kind: 'named',
352
- source: 'solid-js',
353
- },
354
- {
355
- type: 'createContext',
356
- name: 'createContext',
357
- kind: 'named',
358
- source: 'solid-js/web',
359
- },
360
- { type: 'render', name: 'render', kind: 'named', source: 'solid-js/web' },
361
- { type: 'render', name: 'hydrate', kind: 'named', source: 'solid-js/web' },
362
- ];
363
- function getImportSpecifierName(specifier) {
364
- switch (specifier.imported.type) {
365
- case 'Identifier':
366
- return specifier.imported.name;
367
- case 'StringLiteral':
368
- return specifier.imported.value;
369
- }
370
- }
371
- function registerImportSpecifier(state, id, specifier) {
372
- switch (specifier.type) {
373
- case 'ImportDefaultSpecifier': {
374
- if (id.kind === 'default') {
375
- state.registrations.identifiers.set(specifier.local, id);
520
+
521
+ function getDescriptiveName(path, defaultName) {
522
+ let current = path;
523
+ while (current) {
524
+ switch (current.node.type) {
525
+ case 'FunctionDeclaration':
526
+ case 'FunctionExpression': {
527
+ if (current.node.id) {
528
+ return current.node.id.name;
529
+ }
530
+ break;
376
531
  }
377
- break;
378
- }
379
- case 'ImportSpecifier': {
380
- if ((id.kind === 'named' &&
381
- getImportSpecifierName(specifier) === id.name) ||
382
- (id.kind === 'default' &&
383
- getImportSpecifierName(specifier) === 'default')) {
384
- state.registrations.identifiers.set(specifier.local, id);
532
+ case 'VariableDeclarator': {
533
+ if (current.node.id.type === 'Identifier') {
534
+ return current.node.id.name;
535
+ }
536
+ break;
385
537
  }
386
- break;
387
- }
388
- case 'ImportNamespaceSpecifier': {
389
- let current = state.registrations.namespaces.get(specifier.local);
390
- if (!current) {
391
- current = [];
538
+ case 'ClassPrivateMethod':
539
+ case 'ClassMethod':
540
+ case 'ObjectMethod': {
541
+ switch (current.node.key.type) {
542
+ case 'Identifier':
543
+ return current.node.key.name;
544
+ case 'PrivateName':
545
+ return current.node.key.id.name;
546
+ }
547
+ break;
392
548
  }
393
- current.push(id);
394
- state.registrations.namespaces.set(specifier.local, current);
395
- break;
396
549
  }
550
+ current = current.parentPath;
397
551
  }
552
+ return defaultName;
553
+ }
554
+
555
+ function generateUniqueName(path, name) {
556
+ let uid;
557
+ let i = 1;
558
+ do {
559
+ uid = name + '_' + i;
560
+ i++;
561
+ } while (path.scope.hasLabel(uid) ||
562
+ path.scope.hasBinding(uid) ||
563
+ path.scope.hasGlobal(uid) ||
564
+ path.scope.hasReference(uid));
565
+ const program = path.scope.getProgramParent();
566
+ program.references[uid] = true;
567
+ program.uids[uid] = true;
568
+ return t.identifier(uid);
398
569
  }
399
- function registerImportSpecifiers(state, p) {
400
- for (let i = 0, len = state.imports.length; i < len; i++) {
401
- const id = state.imports[i];
402
- if (p.node.source.value === id.source) {
403
- for (let k = 0, klen = p.node.specifiers.length; k < klen; k++) {
404
- registerImportSpecifier(state, id, p.node.specifiers[k]);
570
+
571
+ const REFRESH_JSX_SKIP = /^\s*@refresh jsx-skip\s*$/;
572
+ function shouldSkipJSX(node) {
573
+ // Node without leading comments shouldn't be skipped
574
+ if (node.leadingComments) {
575
+ for (let i = 0, len = node.leadingComments.length; i < len; i++) {
576
+ if (REFRESH_JSX_SKIP.test(node.leadingComments[i].value)) {
577
+ return true;
405
578
  }
406
579
  }
407
580
  }
581
+ return false;
408
582
  }
409
- function captureIdentifiers(state, path) {
410
- path.traverse({
411
- ImportDeclaration(p) {
412
- if (p.node.importKind === 'value') {
413
- registerImportSpecifiers(state, p);
583
+ function skippableJSX(node) {
584
+ return t.addComment(node, 'leading', '@refresh jsx-skip');
585
+ }
586
+ function pushAttribute(state, replacement) {
587
+ const key = 'v' + state.attributes.length;
588
+ state.attributes.push(t.jsxAttribute(t.jsxIdentifier(key), t.jsxExpressionContainer(replacement)));
589
+ return key;
590
+ }
591
+ function pushAttributeAndReplace(state, target, replacement) {
592
+ const key = pushAttribute(state, replacement);
593
+ target.replaceWith(t.memberExpression(state.props, t.identifier(key)));
594
+ }
595
+ function extractJSXExpressionFromNormalAttribute(state, attr) {
596
+ const value = attr.get('value');
597
+ if (isPathValid(value, t.isJSXElement) ||
598
+ isPathValid(value, t.isJSXFragment)) {
599
+ value.replaceWith(t.jsxExpressionContainer(value.node));
600
+ }
601
+ if (isPathValid(value, t.isJSXExpressionContainer)) {
602
+ extractJSXExpressionsFromJSXExpressionContainer(state, value);
603
+ }
604
+ }
605
+ function extractJSXExpressionFromRef(state, attr) {
606
+ const value = attr.get('value');
607
+ if (isPathValid(value, t.isJSXExpressionContainer)) {
608
+ const expr = value.get('expression');
609
+ if (isPathValid(expr, t.isExpression)) {
610
+ const unwrappedIdentifier = unwrapNode(expr.node, t.isIdentifier);
611
+ let replacement;
612
+ if (unwrappedIdentifier) {
613
+ const arg = expr.scope.generateUidIdentifier('arg');
614
+ replacement = t.arrowFunctionExpression([arg], t.blockStatement([
615
+ t.ifStatement(t.binaryExpression('===', t.unaryExpression('typeof', unwrappedIdentifier), t.stringLiteral('function')), t.blockStatement([
616
+ t.expressionStatement(t.callExpression(unwrappedIdentifier, [arg])),
617
+ ]), t.blockStatement([
618
+ t.expressionStatement(t.assignmentExpression('=', unwrappedIdentifier, arg)),
619
+ ])),
620
+ ]));
414
621
  }
415
- },
416
- });
622
+ else {
623
+ replacement = expr.node;
624
+ }
625
+ pushAttributeAndReplace(state, expr, replacement);
626
+ }
627
+ }
417
628
  }
418
- function unwrapExpression(node, key) {
419
- switch (node.type) {
420
- case 'ParenthesizedExpression':
421
- case 'TypeCastExpression':
422
- case 'TSAsExpression':
423
- case 'TSSatisfiesExpression':
424
- case 'TSNonNullExpression':
425
- case 'TSInstantiationExpression':
426
- case 'TSTypeAssertion':
427
- return unwrapExpression(node.expression, key);
428
- default:
429
- return key(node) ? node : undefined;
629
+ function extractJSXExpressionFromUseDirective(state, id, attr) {
630
+ const value = attr.get('value');
631
+ if (isPathValid(value, t.isJSXExpressionContainer)) {
632
+ extractJSXExpressionsFromJSXExpressionContainer(state, value);
430
633
  }
634
+ const key = pushAttribute(state, t.identifier(id.name));
635
+ state.vars.push(t.variableDeclarator(t.identifier(id.name), t.memberExpression(state.props, t.identifier(key))));
431
636
  }
432
- function isIdentifierValidCallee(state, path, callee, target) {
433
- const binding = path.scope.getBindingIdentifier(callee.name);
434
- if (binding) {
435
- const result = state.registrations.identifiers.get(binding);
436
- if (result && result.type === target) {
437
- return true;
637
+ function extractJSXExpressionFromAttribute(state, attr) {
638
+ const key = attr.get('name');
639
+ if (isPathValid(key, t.isJSXIdentifier)) {
640
+ if (key.node.name === 'ref') {
641
+ extractJSXExpressionFromRef(state, attr);
642
+ }
643
+ else {
644
+ extractJSXExpressionFromNormalAttribute(state, attr);
645
+ }
646
+ }
647
+ else if (isPathValid(key, t.isJSXNamespacedName)) {
648
+ if (key.node.namespace.name === 'use') {
649
+ extractJSXExpressionFromUseDirective(state, key.node.name, attr);
650
+ }
651
+ else {
652
+ extractJSXExpressionFromNormalAttribute(state, attr);
438
653
  }
439
654
  }
440
- return false;
441
655
  }
442
- function isPropertyValidCallee(result, target, propName) {
443
- for (let i = 0, len = result.length; i < len; i++) {
444
- const registration = result[i];
445
- if (registration.type === target) {
446
- if (registration.kind === 'default') {
447
- if (propName === 'default') {
448
- return true;
449
- }
450
- }
451
- else if (registration.kind === 'named' &&
452
- registration.name === propName) {
453
- return true;
454
- }
656
+ function extractJSXExpressionsFromAttributes(state, path) {
657
+ const openingElement = path.get('openingElement');
658
+ const attrs = openingElement.get('attributes');
659
+ for (let i = 0, len = attrs.length; i < len; i++) {
660
+ const attr = attrs[i];
661
+ if (isPathValid(attr, t.isJSXAttribute)) {
662
+ extractJSXExpressionFromAttribute(state, attr);
663
+ }
664
+ if (isPathValid(attr, t.isJSXSpreadAttribute)) {
665
+ const arg = attr.get('argument');
666
+ pushAttributeAndReplace(state, arg, arg.node);
455
667
  }
456
668
  }
457
- return false;
458
669
  }
459
- function isMemberExpressionValidCallee(state, path, member, target) {
460
- if (!t.isIdentifier(member.property)) {
461
- return false;
670
+ function convertJSXOpeningToExpression(node) {
671
+ if (t.isJSXIdentifier(node)) {
672
+ return t.identifier(node.name);
673
+ }
674
+ return t.memberExpression(convertJSXOpeningToExpression(node.object), convertJSXOpeningToExpression(node.property));
675
+ }
676
+ function extractJSXExpressionsFromJSXElement(state, path) {
677
+ const openingElement = path.get('openingElement');
678
+ const openingName = openingElement.get('name');
679
+ if ((isPathValid(openingName, t.isJSXIdentifier) &&
680
+ /^[A-Z_]/.test(openingName.node.name)) ||
681
+ isPathValid(openingName, t.isJSXMemberExpression)) {
682
+ const key = pushAttribute(state, convertJSXOpeningToExpression(openingName.node));
683
+ const replacement = t.jsxMemberExpression(t.jsxIdentifier(state.props.name), t.jsxIdentifier(key));
684
+ openingName.replaceWith(replacement);
685
+ const closingElement = path.get('closingElement');
686
+ if (isPathValid(closingElement, t.isJSXClosingElement)) {
687
+ closingElement.get('name').replaceWith(replacement);
688
+ }
462
689
  }
463
- const trueObject = unwrapExpression(member.object, t.isIdentifier);
464
- if (!trueObject) {
465
- return false;
690
+ extractJSXExpressionsFromAttributes(state, path);
691
+ }
692
+ function extractJSXExpressionsFromJSXExpressionContainer(state, child) {
693
+ const expr = child.get('expression');
694
+ if (isPathValid(expr, t.isExpression)) {
695
+ pushAttributeAndReplace(state, expr, expr.node);
466
696
  }
467
- const binding = path.scope.getBindingIdentifier(trueObject.name);
468
- if (!binding) {
469
- return false;
697
+ }
698
+ function extractJSXExpressionsFromJSXSpreadChild(state, child) {
699
+ const arg = child.get('expression');
700
+ pushAttributeAndReplace(state, arg, arg.node);
701
+ }
702
+ function extractJSXExpressions(state, path) {
703
+ if (isPathValid(path, t.isJSXElement)) {
704
+ extractJSXExpressionsFromJSXElement(state, path);
470
705
  }
471
- const result = state.registrations.namespaces.get(binding);
472
- if (!result) {
473
- return false;
706
+ const children = path.get('children');
707
+ for (let i = 0, len = children.length; i < len; i++) {
708
+ const child = children[i];
709
+ if (isPathValid(child, t.isJSXElement) ||
710
+ isPathValid(child, t.isJSXFragment)) {
711
+ extractJSXExpressions(state, child);
712
+ }
713
+ else if (isPathValid(child, t.isJSXExpressionContainer)) {
714
+ extractJSXExpressionsFromJSXExpressionContainer(state, child);
715
+ }
716
+ else if (isPathValid(child, t.isJSXSpreadChild)) {
717
+ extractJSXExpressionsFromJSXSpreadChild(state, child);
718
+ }
474
719
  }
475
- return isPropertyValidCallee(result, target, member.property.name);
476
720
  }
477
- function isValidCallee(state, path, { callee }, target) {
478
- if (t.isV8IntrinsicIdentifier(callee)) {
479
- return false;
721
+ function transformJSX(path) {
722
+ if (shouldSkipJSX(path.node)) {
723
+ return;
480
724
  }
481
- const trueCallee = unwrapExpression(callee, t.isIdentifier);
482
- if (trueCallee) {
483
- return isIdentifierValidCallee(state, path, trueCallee, target);
725
+ const state = {
726
+ props: path.scope.generateUidIdentifier('props'),
727
+ attributes: [],
728
+ vars: [],
729
+ };
730
+ extractJSXExpressions(state, path);
731
+ const descriptiveName = getDescriptiveName(path, 'template');
732
+ const id = generateUniqueName(path, isComponentishName(descriptiveName)
733
+ ? descriptiveName
734
+ : 'JSX_' + descriptiveName);
735
+ const rootPath = getRootStatementPath(path);
736
+ let template = skippableJSX(t.cloneNode(path.node));
737
+ if (state.vars.length) {
738
+ template = t.blockStatement([
739
+ t.variableDeclaration('const', state.vars),
740
+ t.returnStatement(template),
741
+ ]);
484
742
  }
485
- const trueMember = unwrapExpression(callee, t.isMemberExpression);
486
- if (trueMember && !trueMember.computed) {
487
- return isMemberExpressionValidCallee(state, path, trueMember, target);
743
+ const templateComp = t.arrowFunctionExpression([state.props], template);
744
+ if (path.node.loc) {
745
+ templateComp.loc = path.node.loc;
488
746
  }
489
- return false;
747
+ rootPath.insertBefore(t.variableDeclaration('const', [t.variableDeclarator(id, templateComp)]));
748
+ path.replaceWith(skippableJSX(t.jsxElement(t.jsxOpeningElement(t.jsxIdentifier(id.name), [...state.attributes], true), t.jsxClosingElement(t.jsxIdentifier(id.name)), [], true)));
749
+ }
750
+
751
+ // https://github.com/babel/babel/issues/15269
752
+ let generator;
753
+ if (typeof _generator !== 'function') {
754
+ generator = _generator.default;
755
+ }
756
+ else {
757
+ generator = _generator;
758
+ }
759
+ function generateCode(node) {
760
+ return generator(node).code;
761
+ }
762
+
763
+ const CWD = process.cwd();
764
+ function getFile(filename) {
765
+ return path.relative(CWD, filename);
766
+ }
767
+ function createSignatureValue(node) {
768
+ const code = generateCode(node);
769
+ const result = xxHash32(code).toString(16);
770
+ return result;
771
+ }
772
+ function captureIdentifiers(state, path) {
773
+ path.traverse({
774
+ ImportDeclaration(p) {
775
+ if (p.node.importKind === 'value') {
776
+ registerImportSpecifiers(state, p, state.specifiers);
777
+ }
778
+ },
779
+ });
490
780
  }
491
781
  function checkValidRenderCall(path) {
492
782
  let currentPath = path.parentPath;
@@ -504,7 +794,7 @@ function checkValidRenderCall(path) {
504
794
  function fixRenderCalls(state, path) {
505
795
  path.traverse({
506
796
  ExpressionStatement(p) {
507
- const trueCallExpr = unwrapExpression(p.node.expression, t.isCallExpression);
797
+ const trueCallExpr = unwrapNode(p.node.expression, t.isCallExpression);
508
798
  if (trueCallExpr &&
509
799
  checkValidRenderCall(p) &&
510
800
  isValidCallee(state, p, trueCallExpr, 'render')) {
@@ -525,7 +815,7 @@ function wrapComponent(state, path, identifier, component, original = component)
525
815
  if (statementPath) {
526
816
  const registry = createRegistry(state, statementPath);
527
817
  const hotName = t.stringLiteral(identifier.name);
528
- const componentCall = getSolidRefreshIdentifier(state, statementPath, IMPORTS.component);
818
+ const componentCall = getImportIdentifier(state, statementPath, IMPORT_COMPONENT);
529
819
  const properties = [];
530
820
  if (state.filename && original.loc) {
531
821
  const filePath = getFile(state.filename);
@@ -533,7 +823,7 @@ function wrapComponent(state, path, identifier, component, original = component)
533
823
  }
534
824
  if (state.granular) {
535
825
  properties.push(t.objectProperty(t.identifier('signature'), t.stringLiteral(createSignatureValue(component))));
536
- const dependencies = getBindings(path);
826
+ const dependencies = getForeignBindings(path);
537
827
  if (dependencies.length) {
538
828
  const dependencyKeys = [];
539
829
  let id;
@@ -543,18 +833,12 @@ function wrapComponent(state, path, identifier, component, original = component)
543
833
  }
544
834
  properties.push(t.objectProperty(t.identifier('dependencies'), t.arrowFunctionExpression([], t.objectExpression(dependencyKeys))));
545
835
  }
546
- return t.callExpression(componentCall, [
547
- registry,
548
- hotName,
549
- component,
550
- t.objectExpression(properties),
551
- ]);
552
836
  }
553
837
  return t.callExpression(componentCall, [
554
838
  registry,
555
839
  hotName,
556
840
  component,
557
- ...(properties.length ? [t.objectExpression(properties)] : []),
841
+ t.objectExpression(properties),
558
842
  ]);
559
843
  }
560
844
  return component;
@@ -564,21 +848,15 @@ function wrapContext(state, path, identifier, context) {
564
848
  if (statementPath) {
565
849
  const registry = createRegistry(state, statementPath);
566
850
  const hotName = t.stringLiteral(identifier.name);
567
- const contextCall = getSolidRefreshIdentifier(state, statementPath, IMPORTS.context);
851
+ const contextCall = getImportIdentifier(state, statementPath, IMPORT_CONTEXT);
568
852
  return t.callExpression(contextCall, [registry, hotName, context]);
569
853
  }
570
854
  return context;
571
855
  }
572
- function setupProgram(state, path) {
573
- var _a;
856
+ function setupProgram(state, path, comments) {
574
857
  let shouldSkip = false;
575
- const comments = state.file.ast.comments;
576
858
  if (comments) {
577
859
  for (const { value: comment } of comments) {
578
- if (/^\s*@refresh granular\s*$/.test(comment)) {
579
- state.granular = true;
580
- break;
581
- }
582
860
  if (/^\s*@refresh skip\s*$/.test(comment)) {
583
861
  state.processed = true;
584
862
  shouldSkip = true;
@@ -592,39 +870,20 @@ function setupProgram(state, path) {
592
870
  }
593
871
  }
594
872
  captureIdentifiers(state, path);
595
- if (!shouldSkip && ((_a = state.opts.fixRender) !== null && _a !== void 0 ? _a : true)) {
873
+ if (!shouldSkip && state.fixRender) {
596
874
  fixRenderCalls(state, path);
597
875
  }
598
876
  }
599
- function transformExportNamedDeclaration(state, path) {
600
- if (state.processed) {
601
- return;
602
- }
603
- const decl = path.node.declaration;
604
- // Check if declaration is FunctionDeclaration
605
- if (t.isFunctionDeclaration(decl) &&
606
- !(decl.generator || decl.async) &&
607
- // Might be component-like, but the only valid components
608
- // have zero or one parameter
609
- decl.params.length < 2) {
610
- // Check if the declaration has an identifier, and then check
611
- // if the name is component-ish
612
- if (decl.id && isComponentishName(decl.id.name)) {
613
- path.node.declaration = t.variableDeclaration('const', [
614
- t.variableDeclarator(decl.id, wrapComponent(state, path, decl.id, t.functionExpression(decl.id, decl.params, decl.body), decl)),
615
- ]);
616
- }
617
- }
618
- }
619
877
  function isStatementTopLevel(path) {
878
+ let blockParent = path.scope.getBlockParent();
620
879
  const programParent = path.scope.getProgramParent();
621
- const blockParent = path.scope.getBlockParent();
880
+ // a FunctionDeclaration binding refers to itself as the block parent
881
+ if (blockParent.path === path) {
882
+ blockParent = blockParent.parent;
883
+ }
622
884
  return programParent === blockParent;
623
885
  }
624
886
  function transformVariableDeclarator(state, path) {
625
- if (state.processed) {
626
- return;
627
- }
628
887
  if (path.parentPath.isVariableDeclaration() &&
629
888
  !isStatementTopLevel(path.parentPath)) {
630
889
  return;
@@ -635,8 +894,8 @@ function transformVariableDeclarator(state, path) {
635
894
  return;
636
895
  }
637
896
  if (isComponentishName(identifier.name)) {
638
- const trueFuncExpr = unwrapExpression(init, t.isFunctionExpression) ||
639
- unwrapExpression(init, t.isArrowFunctionExpression);
897
+ const trueFuncExpr = unwrapNode(init, t.isFunctionExpression) ||
898
+ unwrapNode(init, t.isArrowFunctionExpression);
640
899
  // Check for valid FunctionExpression or ArrowFunctionExpression
641
900
  if (trueFuncExpr &&
642
901
  // Must not be async or generator
@@ -648,73 +907,111 @@ function transformVariableDeclarator(state, path) {
648
907
  }
649
908
  }
650
909
  // For `createContext` calls
651
- const trueCallExpr = unwrapExpression(init, t.isCallExpression);
910
+ const trueCallExpr = unwrapNode(init, t.isCallExpression);
652
911
  if (trueCallExpr &&
653
912
  isValidCallee(state, path, trueCallExpr, 'createContext')) {
654
913
  path.node.init = wrapContext(state, path, identifier, trueCallExpr);
655
914
  }
915
+ path.skip();
916
+ }
917
+ function transformFunctionDeclaration(state, path) {
918
+ if (isStatementTopLevel(path)) {
919
+ const decl = path.node;
920
+ // Check if declaration is FunctionDeclaration
921
+ if (
922
+ // Check if the declaration has an identifier, and then check
923
+ decl.id &&
924
+ // if the name is component-ish
925
+ isComponentishName(decl.id.name) &&
926
+ !(decl.generator || decl.async) &&
927
+ // Might be component-like, but the only valid components
928
+ // have zero or one parameter
929
+ decl.params.length < 2) {
930
+ path.replaceWith(t.variableDeclaration('const', [
931
+ t.variableDeclarator(decl.id, wrapComponent(state, path, decl.id, t.functionExpression(decl.id, decl.params, decl.body), decl)),
932
+ ]));
933
+ path.skip();
934
+ }
935
+ }
936
+ }
937
+ function bubbleFunctionDeclaration(program, path) {
938
+ if (isStatementTopLevel(path)) {
939
+ const decl = path.node;
940
+ // Check if declaration is FunctionDeclaration
941
+ if (
942
+ // Check if the declaration has an identifier, and then check
943
+ decl.id &&
944
+ // if the name is component-ish
945
+ isComponentishName(decl.id.name) &&
946
+ !(decl.generator || decl.async) &&
947
+ // Might be component-like, but the only valid components
948
+ // have zero or one parameter
949
+ decl.params.length < 2) {
950
+ const first = program.get('body')[0];
951
+ const [tmp] = first.insertBefore(decl);
952
+ tmp.skip();
953
+ if (path.parentPath.isExportNamedDeclaration()) {
954
+ path.parentPath.replaceWith(t.exportNamedDeclaration(undefined, [
955
+ t.exportSpecifier(decl.id, decl.id),
956
+ ]));
957
+ }
958
+ else if (path.parentPath.isExportDefaultDeclaration()) {
959
+ path.replaceWith(decl.id);
960
+ }
961
+ else {
962
+ path.remove();
963
+ }
964
+ }
965
+ }
656
966
  }
657
967
  function solidRefreshPlugin() {
658
968
  return {
659
- name: 'Solid Refresh',
660
- pre() {
661
- this.hooks = new Map();
662
- this.processed = false;
663
- this.granular = false;
664
- this.registrations = {
665
- identifiers: new Map(),
666
- namespaces: new Map(),
667
- };
668
- this.imports = [...IMPORT_IDENTITIES, ...(this.opts.imports || [])];
669
- },
969
+ name: 'solid-refresh',
670
970
  visitor: {
671
- Program(programPath, state) {
672
- setupProgram(state, programPath);
971
+ Program(programPath, context) {
972
+ var _a, _b;
973
+ const state = {
974
+ granular: (_a = context.opts.granular) !== null && _a !== void 0 ? _a : true,
975
+ opts: context.opts,
976
+ specifiers: [...IMPORT_SPECIFIERS],
977
+ imports: new Map(),
978
+ registrations: {
979
+ identifiers: new Map(),
980
+ namespaces: new Map(),
981
+ },
982
+ processed: false,
983
+ filename: context.filename,
984
+ bundler: context.opts.bundler || 'standard',
985
+ fixRender: (_b = context.opts.fixRender) !== null && _b !== void 0 ? _b : true,
986
+ };
987
+ setupProgram(state, programPath, context.file.ast.comments);
988
+ if (state.processed) {
989
+ return;
990
+ }
673
991
  programPath.traverse({
674
- ExportNamedDeclaration(path) {
675
- transformExportNamedDeclaration(state, path);
992
+ FunctionDeclaration(path) {
993
+ bubbleFunctionDeclaration(programPath, path);
676
994
  },
995
+ });
996
+ programPath.scope.crawl();
997
+ programPath.traverse({
998
+ JSXElement(path) {
999
+ transformJSX(path);
1000
+ },
1001
+ JSXFragment(path) {
1002
+ transformJSX(path);
1003
+ },
1004
+ });
1005
+ programPath.scope.crawl();
1006
+ programPath.traverse({
677
1007
  VariableDeclarator(path) {
678
1008
  transformVariableDeclarator(state, path);
679
1009
  },
680
1010
  FunctionDeclaration(path) {
681
- if (state.processed) {
682
- return;
683
- }
684
- if (path.parentPath.isProgram() ||
685
- path.parentPath.isExportDefaultDeclaration()) {
686
- const decl = path.node;
687
- // Check if declaration is FunctionDeclaration
688
- if (
689
- // Check if the declaration has an identifier, and then check
690
- decl.id &&
691
- // if the name is component-ish
692
- isComponentishName(decl.id.name) &&
693
- !(decl.generator || decl.async) &&
694
- // Might be component-like, but the only valid components
695
- // have zero or one parameter
696
- decl.params.length < 2) {
697
- const replacement = wrapComponent(state, path, decl.id, t.functionExpression(decl.id, decl.params, decl.body), decl);
698
- const newDecl = t.variableDeclaration('var', [
699
- t.variableDeclarator(decl.id, replacement),
700
- ]);
701
- if (path.parentPath.isExportDefaultDeclaration()) {
702
- const parent = path.parentPath
703
- .parentPath;
704
- const first = parent.get('body')[0];
705
- first.insertBefore(newDecl);
706
- path.replaceWith(decl.id);
707
- }
708
- else {
709
- const parent = path.parentPath;
710
- const first = parent.get('body')[0];
711
- first.insertBefore(newDecl);
712
- path.remove();
713
- }
714
- }
715
- }
1011
+ transformFunctionDeclaration(state, path);
716
1012
  },
717
1013
  });
1014
+ programPath.scope.crawl();
718
1015
  },
719
1016
  },
720
1017
  };