solid-refresh 0.6.3 → 0.7.1

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 +39 -11
  2. package/dist/babel.cjs +583 -286
  3. package/dist/babel.mjs +583 -286
  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 +40 -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 +2 -25
  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,67 +848,44 @@ 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;
858
+ let isDone = false;
576
859
  if (comments) {
577
860
  for (const { value: comment } of comments) {
578
- if (/^\s*@refresh granular\s*$/.test(comment)) {
579
- state.granular = true;
580
- break;
581
- }
582
861
  if (/^\s*@refresh skip\s*$/.test(comment)) {
583
- state.processed = true;
862
+ isDone = true;
584
863
  shouldSkip = true;
585
864
  break;
586
865
  }
587
866
  if (/^\s*@refresh reload\s*$/.test(comment)) {
588
- state.processed = true;
867
+ isDone = true;
589
868
  path.pushContainer('body', getHMRDeclineCall(state, path));
590
869
  break;
591
870
  }
592
871
  }
593
872
  }
594
- captureIdentifiers(state, path);
595
- if (!shouldSkip && ((_a = state.opts.fixRender) !== null && _a !== void 0 ? _a : true)) {
873
+ if (!shouldSkip && state.fixRender) {
874
+ captureIdentifiers(state, path);
596
875
  fixRenderCalls(state, path);
597
876
  }
598
- }
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
- }
877
+ return isDone;
618
878
  }
619
879
  function isStatementTopLevel(path) {
880
+ let blockParent = path.scope.getBlockParent();
620
881
  const programParent = path.scope.getProgramParent();
621
- const blockParent = path.scope.getBlockParent();
882
+ // a FunctionDeclaration binding refers to itself as the block parent
883
+ if (blockParent.path === path) {
884
+ blockParent = blockParent.parent;
885
+ }
622
886
  return programParent === blockParent;
623
887
  }
624
888
  function transformVariableDeclarator(state, path) {
625
- if (state.processed) {
626
- return;
627
- }
628
889
  if (path.parentPath.isVariableDeclaration() &&
629
890
  !isStatementTopLevel(path.parentPath)) {
630
891
  return;
@@ -635,8 +896,8 @@ function transformVariableDeclarator(state, path) {
635
896
  return;
636
897
  }
637
898
  if (isComponentishName(identifier.name)) {
638
- const trueFuncExpr = unwrapExpression(init, t.isFunctionExpression) ||
639
- unwrapExpression(init, t.isArrowFunctionExpression);
899
+ const trueFuncExpr = unwrapNode(init, t.isFunctionExpression) ||
900
+ unwrapNode(init, t.isArrowFunctionExpression);
640
901
  // Check for valid FunctionExpression or ArrowFunctionExpression
641
902
  if (trueFuncExpr &&
642
903
  // Must not be async or generator
@@ -648,73 +909,109 @@ function transformVariableDeclarator(state, path) {
648
909
  }
649
910
  }
650
911
  // For `createContext` calls
651
- const trueCallExpr = unwrapExpression(init, t.isCallExpression);
912
+ const trueCallExpr = unwrapNode(init, t.isCallExpression);
652
913
  if (trueCallExpr &&
653
914
  isValidCallee(state, path, trueCallExpr, 'createContext')) {
654
915
  path.node.init = wrapContext(state, path, identifier, trueCallExpr);
655
916
  }
917
+ path.skip();
918
+ }
919
+ function transformFunctionDeclaration(state, path) {
920
+ if (isStatementTopLevel(path)) {
921
+ const decl = path.node;
922
+ // Check if declaration is FunctionDeclaration
923
+ if (
924
+ // Check if the declaration has an identifier, and then check
925
+ decl.id &&
926
+ // if the name is component-ish
927
+ isComponentishName(decl.id.name) &&
928
+ !(decl.generator || decl.async) &&
929
+ // Might be component-like, but the only valid components
930
+ // have zero or one parameter
931
+ decl.params.length < 2) {
932
+ path.replaceWith(t.variableDeclaration('const', [
933
+ t.variableDeclarator(decl.id, wrapComponent(state, path, decl.id, t.functionExpression(decl.id, decl.params, decl.body), decl)),
934
+ ]));
935
+ path.skip();
936
+ }
937
+ }
938
+ }
939
+ function bubbleFunctionDeclaration(program, path) {
940
+ if (isStatementTopLevel(path)) {
941
+ const decl = path.node;
942
+ // Check if declaration is FunctionDeclaration
943
+ if (
944
+ // Check if the declaration has an identifier, and then check
945
+ decl.id &&
946
+ // if the name is component-ish
947
+ isComponentishName(decl.id.name) &&
948
+ !(decl.generator || decl.async) &&
949
+ // Might be component-like, but the only valid components
950
+ // have zero or one parameter
951
+ decl.params.length < 2) {
952
+ const first = program.get('body')[0];
953
+ const [tmp] = first.insertBefore(decl);
954
+ tmp.skip();
955
+ if (path.parentPath.isExportNamedDeclaration()) {
956
+ path.parentPath.replaceWith(t.exportNamedDeclaration(undefined, [
957
+ t.exportSpecifier(decl.id, decl.id),
958
+ ]));
959
+ }
960
+ else if (path.parentPath.isExportDefaultDeclaration()) {
961
+ path.replaceWith(decl.id);
962
+ }
963
+ else {
964
+ path.remove();
965
+ }
966
+ }
967
+ }
656
968
  }
657
969
  function solidRefreshPlugin() {
658
970
  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
- },
971
+ name: 'solid-refresh',
670
972
  visitor: {
671
- Program(programPath, state) {
672
- setupProgram(state, programPath);
973
+ Program(programPath, context) {
974
+ var _a, _b;
975
+ const state = {
976
+ granular: (_a = context.opts.granular) !== null && _a !== void 0 ? _a : true,
977
+ opts: context.opts,
978
+ specifiers: [...IMPORT_SPECIFIERS],
979
+ imports: new Map(),
980
+ registrations: {
981
+ identifiers: new Map(),
982
+ namespaces: new Map(),
983
+ },
984
+ filename: context.filename,
985
+ bundler: context.opts.bundler || 'standard',
986
+ fixRender: (_b = context.opts.fixRender) !== null && _b !== void 0 ? _b : true,
987
+ };
988
+ if (setupProgram(state, programPath, context.file.ast.comments)) {
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
  };