turbo-rails 0.7.1 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d693019f1ad4faadbaad84b55386d17aa4874d33be169d7641fdd4ac47ac0eb0
4
- data.tar.gz: 01562da91f25cad722e92ece91c21a5280cd91cc61a8359510573bfb5d205454
3
+ metadata.gz: d9a5c5d9d67d381421fba7c92a4a3bc2428d8cdeea8bd6787aed126ba8192f0b
4
+ data.tar.gz: 6f4633e3908cad563c384fb2a8418ddcf651f0700ea4ffdb284ab3070e946140
5
5
  SHA512:
6
- metadata.gz: 5d8c769812b11bfafa30a960aa65f78fb5a9b6c9b7eaafad62a88fc305169be864baaec58d9012f56cd5ec1fbd402382154fdb60a75708e77d0a102e254d0976
7
- data.tar.gz: 13020b72abb6e42edd6cbf16af8ac5d84340c68f40197f37cebbb9ffc76c8fc3d9c0aba396986947539de051ee5029f6945e382a028053290cf905e566af0298
6
+ metadata.gz: 348b92c88b2f8dfd28b6c5628ef83619f40f9cde05cc177020a8808226507b0542651254600dab8d1739ce398805d96696d8b59ecc0615ffffa18b86a2834f09
7
+ data.tar.gz: 02ba6add278e326446c843f6341f119b85c85d3c9153eed10b28c21d7c7df188f25052b9a155f84dc07a84dcffad90f46726e2559c89ad4be6a6424c87f11b9c
@@ -361,8 +361,10 @@ class FetchRequest {
361
361
  const response = await fetch(this.url.href, fetchOptions);
362
362
  return await this.receive(response);
363
363
  } catch (error) {
364
- this.delegate.requestErrored(this, error);
365
- throw error;
364
+ if (error.name !== "AbortError") {
365
+ this.delegate.requestErrored(this, error);
366
+ throw error;
367
+ }
366
368
  } finally {
367
369
  this.delegate.requestFinished(this);
368
370
  }
@@ -385,13 +387,15 @@ class FetchRequest {
385
387
  return fetchResponse;
386
388
  }
387
389
  get fetchOptions() {
390
+ var _a;
388
391
  return {
389
392
  method: FetchMethod[this.method].toUpperCase(),
390
393
  credentials: "same-origin",
391
394
  headers: this.headers,
392
395
  redirect: "follow",
393
396
  body: this.body,
394
- signal: this.abortSignal
397
+ signal: this.abortSignal,
398
+ referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
395
399
  };
396
400
  }
397
401
  get defaultHeaders() {
@@ -544,7 +548,8 @@ class FormSubmission {
544
548
  }
545
549
  get action() {
546
550
  var _a;
547
- return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.action;
551
+ const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
552
+ return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
548
553
  }
549
554
  get location() {
550
555
  return expandURL(this.action);
@@ -691,11 +696,7 @@ class Snapshot {
691
696
  return this.getElementForAnchor(anchor) != null;
692
697
  }
693
698
  getElementForAnchor(anchor) {
694
- try {
695
- return this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`);
696
- } catch (_a) {
697
- return null;
698
- }
699
+ return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
699
700
  }
700
701
  get isConnected() {
701
702
  return this.element.isConnected;
@@ -785,6 +786,12 @@ class View {
785
786
  scrollToPosition({x: x, y: y}) {
786
787
  this.scrollRoot.scrollTo(x, y);
787
788
  }
789
+ scrollToTop() {
790
+ this.scrollToPosition({
791
+ x: 0,
792
+ y: 0
793
+ });
794
+ }
788
795
  get scrollRoot() {
789
796
  return window;
790
797
  }
@@ -1354,6 +1361,9 @@ class Visit {
1354
1361
  get restorationData() {
1355
1362
  return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
1356
1363
  }
1364
+ get silent() {
1365
+ return this.isSamePage;
1366
+ }
1357
1367
  start() {
1358
1368
  if (this.state == VisitState.initialized) {
1359
1369
  this.recordTimingMetric(TimingMetric.visitStart);
@@ -1377,6 +1387,7 @@ class Visit {
1377
1387
  this.state = VisitState.completed;
1378
1388
  this.adapter.visitCompleted(this);
1379
1389
  this.delegate.visitCompleted(this);
1390
+ this.followRedirect();
1380
1391
  }
1381
1392
  }
1382
1393
  fail() {
@@ -1483,8 +1494,10 @@ class Visit {
1483
1494
  }
1484
1495
  followRedirect() {
1485
1496
  if (this.redirectedToLocation && !this.followedRedirect) {
1486
- this.location = this.redirectedToLocation;
1487
- this.history.replace(this.redirectedToLocation, this.restorationIdentifier);
1497
+ this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1498
+ action: "replace",
1499
+ response: this.response
1500
+ });
1488
1501
  this.followedRedirect = true;
1489
1502
  }
1490
1503
  }
@@ -1538,9 +1551,9 @@ class Visit {
1538
1551
  performScroll() {
1539
1552
  if (!this.scrolled) {
1540
1553
  if (this.action == "restore") {
1541
- this.scrollToRestoredPosition() || this.scrollToAnchor() || this.scrollToTop();
1554
+ this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
1542
1555
  } else {
1543
- this.scrollToAnchor() || this.scrollToTop();
1556
+ this.scrollToAnchor() || this.view.scrollToTop();
1544
1557
  }
1545
1558
  if (this.isSamePage) {
1546
1559
  this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
@@ -1562,12 +1575,6 @@ class Visit {
1562
1575
  return true;
1563
1576
  }
1564
1577
  }
1565
- scrollToTop() {
1566
- this.view.scrollToPosition({
1567
- x: 0,
1568
- y: 0
1569
- });
1570
- }
1571
1578
  recordTimingMetric(metric) {
1572
1579
  this.timingMetrics[metric] = (new Date).getTime();
1573
1580
  }
@@ -1666,14 +1673,20 @@ class BrowserAdapter {
1666
1673
  this.progressBar.setValue(1);
1667
1674
  this.hideProgressBar();
1668
1675
  }
1669
- visitCompleted(visit) {
1670
- visit.followRedirect();
1671
- }
1676
+ visitCompleted(visit) {}
1672
1677
  pageInvalidated() {
1673
1678
  this.reload();
1674
1679
  }
1675
1680
  visitFailed(visit) {}
1676
1681
  visitRendered(visit) {}
1682
+ formSubmissionStarted(formSubmission) {
1683
+ this.progressBar.setValue(0);
1684
+ this.showProgressBarAfterDelay();
1685
+ }
1686
+ formSubmissionFinished(formSubmission) {
1687
+ this.progressBar.setValue(1);
1688
+ this.hideProgressBar();
1689
+ }
1677
1690
  showProgressBarAfterDelay() {
1678
1691
  this.progressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
1679
1692
  }
@@ -1891,7 +1904,8 @@ class LinkClickObserver {
1891
1904
  };
1892
1905
  this.clickBubbled = event => {
1893
1906
  if (this.clickEventIsSignificant(event)) {
1894
- const link = this.findLinkFromClickTarget(event.target);
1907
+ const target = event.composedPath && event.composedPath()[0] || event.target;
1908
+ const link = this.findLinkFromClickTarget(target);
1895
1909
  if (link) {
1896
1910
  const location = this.getLocationForLink(link);
1897
1911
  if (this.delegate.willFollowLinkToLocation(link, location)) {
@@ -1937,7 +1951,7 @@ class Navigator {
1937
1951
  this.delegate = delegate;
1938
1952
  }
1939
1953
  proposeVisit(location, options = {}) {
1940
- if (this.delegate.allowsVisitingLocation(location)) {
1954
+ if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
1941
1955
  this.delegate.visitProposedToLocation(location, options);
1942
1956
  }
1943
1957
  }
@@ -1978,7 +1992,11 @@ class Navigator {
1978
1992
  get history() {
1979
1993
  return this.delegate.history;
1980
1994
  }
1981
- formSubmissionStarted(formSubmission) {}
1995
+ formSubmissionStarted(formSubmission) {
1996
+ if (typeof this.adapter.formSubmissionStarted === "function") {
1997
+ this.adapter.formSubmissionStarted(formSubmission);
1998
+ }
1999
+ }
1982
2000
  async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
1983
2001
  if (formSubmission == this.formSubmission) {
1984
2002
  const responseHTML = await fetchResponse.responseHTML;
@@ -2001,14 +2019,23 @@ class Navigator {
2001
2019
  const responseHTML = await fetchResponse.responseHTML;
2002
2020
  if (responseHTML) {
2003
2021
  const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2004
- await this.view.renderPage(snapshot);
2022
+ if (fetchResponse.serverError) {
2023
+ await this.view.renderError(snapshot);
2024
+ } else {
2025
+ await this.view.renderPage(snapshot);
2026
+ }
2027
+ this.view.scrollToTop();
2005
2028
  this.view.clearSnapshotCache();
2006
2029
  }
2007
2030
  }
2008
2031
  formSubmissionErrored(formSubmission, error) {
2009
2032
  console.error(error);
2010
2033
  }
2011
- formSubmissionFinished(formSubmission) {}
2034
+ formSubmissionFinished(formSubmission) {
2035
+ if (typeof this.adapter.formSubmissionFinished === "function") {
2036
+ this.adapter.formSubmissionFinished(formSubmission);
2037
+ }
2038
+ }
2012
2039
  visitStarted(visit) {
2013
2040
  this.delegate.visitStarted(visit);
2014
2041
  }
@@ -2016,7 +2043,10 @@ class Navigator {
2016
2043
  this.delegate.visitCompleted(visit);
2017
2044
  }
2018
2045
  locationWithActionIsSamePage(location, action) {
2019
- return getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) && (getAnchor(location) != null || action == "restore");
2046
+ const anchor = getAnchor(location);
2047
+ const currentAnchor = getAnchor(this.view.lastRenderedLocation);
2048
+ const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
2049
+ return action !== "replace" && getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
2020
2050
  }
2021
2051
  visitScrolledToSamePageLocation(oldURL, newURL) {
2022
2052
  this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
@@ -2408,6 +2438,7 @@ class Session {
2408
2438
  this.scrollObserver = new ScrollObserver(this);
2409
2439
  this.streamObserver = new StreamObserver(this);
2410
2440
  this.frameRedirector = new FrameRedirector(document.documentElement);
2441
+ this.drive = true;
2411
2442
  this.enabled = true;
2412
2443
  this.progressBarDelay = 500;
2413
2444
  this.started = false;
@@ -2485,7 +2516,7 @@ class Session {
2485
2516
  });
2486
2517
  }
2487
2518
  willFollowLinkToLocation(link, location) {
2488
- return elementIsNavigable(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
2519
+ return this.elementDriveEnabled(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
2489
2520
  }
2490
2521
  followedLinkToLocation(link, location) {
2491
2522
  const action = this.getActionForLink(link);
@@ -2494,13 +2525,12 @@ class Session {
2494
2525
  });
2495
2526
  }
2496
2527
  convertLinkWithMethodClickToFormSubmission(link) {
2497
- var _a;
2498
2528
  const linkMethod = link.getAttribute("data-turbo-method");
2499
2529
  if (linkMethod) {
2500
2530
  const form = document.createElement("form");
2501
2531
  form.method = linkMethod;
2502
2532
  form.action = link.getAttribute("href") || "undefined";
2503
- (_a = link.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(form, link);
2533
+ document.body.appendChild(form);
2504
2534
  return dispatch("submit", {
2505
2535
  cancelable: true,
2506
2536
  target: form
@@ -2509,8 +2539,8 @@ class Session {
2509
2539
  return false;
2510
2540
  }
2511
2541
  }
2512
- allowsVisitingLocation(location) {
2513
- return this.applicationAllowsVisitingLocation(location);
2542
+ allowsVisitingLocationWithAction(location, action) {
2543
+ return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
2514
2544
  }
2515
2545
  visitProposedToLocation(location, options) {
2516
2546
  extendURLWithDeprecatedProperties(location);
@@ -2518,7 +2548,9 @@ class Session {
2518
2548
  }
2519
2549
  visitStarted(visit) {
2520
2550
  extendURLWithDeprecatedProperties(visit.location);
2521
- this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
2551
+ if (!visit.silent) {
2552
+ this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
2553
+ }
2522
2554
  }
2523
2555
  visitCompleted(visit) {
2524
2556
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
@@ -2530,7 +2562,7 @@ class Session {
2530
2562
  this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
2531
2563
  }
2532
2564
  willSubmitForm(form, submitter) {
2533
- return elementIsNavigable(form) && elementIsNavigable(submitter);
2565
+ return this.elementDriveEnabled(form) && this.elementDriveEnabled(submitter);
2534
2566
  }
2535
2567
  formSubmitted(form, submitter) {
2536
2568
  this.navigator.submitForm(form, submitter);
@@ -2549,7 +2581,10 @@ class Session {
2549
2581
  this.renderStreamMessage(message);
2550
2582
  }
2551
2583
  viewWillCacheSnapshot() {
2552
- this.notifyApplicationBeforeCachingSnapshot();
2584
+ var _a;
2585
+ if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
2586
+ this.notifyApplicationBeforeCachingSnapshot();
2587
+ }
2553
2588
  }
2554
2589
  allowsImmediateRender({element: element}, resume) {
2555
2590
  const event = this.notifyApplicationBeforeRender(element, resume);
@@ -2562,6 +2597,12 @@ class Session {
2562
2597
  viewInvalidated() {
2563
2598
  this.adapter.pageInvalidated();
2564
2599
  }
2600
+ frameLoaded(frame) {
2601
+ this.notifyApplicationAfterFrameLoad(frame);
2602
+ }
2603
+ frameRendered(fetchResponse, frame) {
2604
+ this.notifyApplicationAfterFrameRender(fetchResponse, frame);
2605
+ }
2565
2606
  applicationAllowsFollowingLinkToLocation(link, location) {
2566
2607
  const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
2567
2608
  return !event.defaultPrevented;
@@ -2624,6 +2665,36 @@ class Session {
2624
2665
  newURL: newURL.toString()
2625
2666
  }));
2626
2667
  }
2668
+ notifyApplicationAfterFrameLoad(frame) {
2669
+ return dispatch("turbo:frame-load", {
2670
+ target: frame
2671
+ });
2672
+ }
2673
+ notifyApplicationAfterFrameRender(fetchResponse, frame) {
2674
+ return dispatch("turbo:frame-render", {
2675
+ detail: {
2676
+ fetchResponse: fetchResponse
2677
+ },
2678
+ target: frame,
2679
+ cancelable: true
2680
+ });
2681
+ }
2682
+ elementDriveEnabled(element) {
2683
+ const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
2684
+ if (this.drive) {
2685
+ if (container) {
2686
+ return container.getAttribute("data-turbo") != "false";
2687
+ } else {
2688
+ return true;
2689
+ }
2690
+ } else {
2691
+ if (container) {
2692
+ return container.getAttribute("data-turbo") == "true";
2693
+ } else {
2694
+ return false;
2695
+ }
2696
+ }
2697
+ }
2627
2698
  getActionForLink(link) {
2628
2699
  const action = link.getAttribute("data-turbo-action");
2629
2700
  return isAction(action) ? action : "advance";
@@ -2636,15 +2707,6 @@ class Session {
2636
2707
  }
2637
2708
  }
2638
2709
 
2639
- function elementIsNavigable(element) {
2640
- const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
2641
- if (container) {
2642
- return container.getAttribute("data-turbo") != "false";
2643
- } else {
2644
- return true;
2645
- }
2646
- }
2647
-
2648
2710
  function extendURLWithDeprecatedProperties(url) {
2649
2711
  Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
2650
2712
  }
@@ -2657,6 +2719,58 @@ const deprecatedLocationPropertyDescriptors = {
2657
2719
  }
2658
2720
  };
2659
2721
 
2722
+ const session = new Session;
2723
+
2724
+ const {navigator: navigator} = session;
2725
+
2726
+ function start() {
2727
+ session.start();
2728
+ }
2729
+
2730
+ function registerAdapter(adapter) {
2731
+ session.registerAdapter(adapter);
2732
+ }
2733
+
2734
+ function visit(location, options) {
2735
+ session.visit(location, options);
2736
+ }
2737
+
2738
+ function connectStreamSource(source) {
2739
+ session.connectStreamSource(source);
2740
+ }
2741
+
2742
+ function disconnectStreamSource(source) {
2743
+ session.disconnectStreamSource(source);
2744
+ }
2745
+
2746
+ function renderStreamMessage(message) {
2747
+ session.renderStreamMessage(message);
2748
+ }
2749
+
2750
+ function clearCache() {
2751
+ session.clearCache();
2752
+ }
2753
+
2754
+ function setProgressBarDelay(delay) {
2755
+ session.setProgressBarDelay(delay);
2756
+ }
2757
+
2758
+ var Turbo = Object.freeze({
2759
+ __proto__: null,
2760
+ navigator: navigator,
2761
+ session: session,
2762
+ PageRenderer: PageRenderer,
2763
+ PageSnapshot: PageSnapshot,
2764
+ start: start,
2765
+ registerAdapter: registerAdapter,
2766
+ visit: visit,
2767
+ connectStreamSource: connectStreamSource,
2768
+ disconnectStreamSource: disconnectStreamSource,
2769
+ renderStreamMessage: renderStreamMessage,
2770
+ clearCache: clearCache,
2771
+ setProgressBarDelay: setProgressBarDelay
2772
+ });
2773
+
2660
2774
  class FrameController {
2661
2775
  constructor(element) {
2662
2776
  this.resolveVisitPromise = () => {};
@@ -2672,6 +2786,7 @@ class FrameController {
2672
2786
  connect() {
2673
2787
  if (!this.connected) {
2674
2788
  this.connected = true;
2789
+ this.reloadable = false;
2675
2790
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2676
2791
  this.appearanceObserver.start();
2677
2792
  }
@@ -2707,7 +2822,7 @@ class FrameController {
2707
2822
  }
2708
2823
  }
2709
2824
  async loadSourceURL() {
2710
- if (!this.settingSourceURL && this.enabled && this.isActive && this.sourceURL != this.currentURL) {
2825
+ if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
2711
2826
  const previousURL = this.currentURL;
2712
2827
  this.currentURL = this.sourceURL;
2713
2828
  if (this.sourceURL) {
@@ -2716,6 +2831,7 @@ class FrameController {
2716
2831
  this.appearanceObserver.stop();
2717
2832
  await this.element.loaded;
2718
2833
  this.hasBeenLoaded = true;
2834
+ session.frameLoaded(this.element);
2719
2835
  } catch (error) {
2720
2836
  this.currentURL = previousURL;
2721
2837
  throw error;
@@ -2735,6 +2851,7 @@ class FrameController {
2735
2851
  const renderer = new FrameRenderer(this.view.snapshot, snapshot, false);
2736
2852
  if (this.view.renderPromise) await this.view.renderPromise;
2737
2853
  await this.view.render(renderer);
2854
+ session.frameRendered(fetchResponse, this.element);
2738
2855
  }
2739
2856
  } catch (error) {
2740
2857
  console.error(error);
@@ -2752,6 +2869,7 @@ class FrameController {
2752
2869
  }
2753
2870
  }
2754
2871
  linkClickIntercepted(element, url) {
2872
+ this.reloadable = true;
2755
2873
  this.navigateFrame(element, url);
2756
2874
  }
2757
2875
  shouldInterceptFormSubmission(element, submitter) {
@@ -2761,6 +2879,7 @@ class FrameController {
2761
2879
  if (this.formSubmission) {
2762
2880
  this.formSubmission.stop();
2763
2881
  }
2882
+ this.reloadable = false;
2764
2883
  this.formSubmission = new FormSubmission(this, element, submitter);
2765
2884
  if (this.formSubmission.fetchRequest.isIdempotent) {
2766
2885
  this.navigateFrame(element, this.formSubmission.fetchRequest.url.href);
@@ -2864,10 +2983,10 @@ class FrameController {
2864
2983
  return !frameElement.disabled;
2865
2984
  }
2866
2985
  }
2867
- if (!elementIsNavigable(element)) {
2986
+ if (!session.elementDriveEnabled(element)) {
2868
2987
  return false;
2869
2988
  }
2870
- if (submitter && !elementIsNavigable(submitter)) {
2989
+ if (submitter && !session.elementDriveEnabled(submitter)) {
2871
2990
  return false;
2872
2991
  }
2873
2992
  return true;
@@ -2883,6 +3002,18 @@ class FrameController {
2883
3002
  return this.element.src;
2884
3003
  }
2885
3004
  }
3005
+ get reloadable() {
3006
+ const frame = this.findFrameElement(this.element);
3007
+ return frame.hasAttribute("reloadable");
3008
+ }
3009
+ set reloadable(value) {
3010
+ const frame = this.findFrameElement(this.element);
3011
+ if (value) {
3012
+ frame.setAttribute("reloadable", "");
3013
+ } else {
3014
+ frame.removeAttribute("reloadable");
3015
+ }
3016
+ }
2886
3017
  set sourceURL(sourceURL) {
2887
3018
  this.settingSourceURL = true;
2888
3019
  this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
@@ -3089,57 +3220,6 @@ customElements.define("turbo-stream", StreamElement);
3089
3220
  }
3090
3221
  })();
3091
3222
 
3092
- const session = new Session;
3093
-
3094
- const {navigator: navigator} = session;
3095
-
3096
- function start() {
3097
- session.start();
3098
- }
3099
-
3100
- function registerAdapter(adapter) {
3101
- session.registerAdapter(adapter);
3102
- }
3103
-
3104
- function visit(location, options) {
3105
- session.visit(location, options);
3106
- }
3107
-
3108
- function connectStreamSource(source) {
3109
- session.connectStreamSource(source);
3110
- }
3111
-
3112
- function disconnectStreamSource(source) {
3113
- session.disconnectStreamSource(source);
3114
- }
3115
-
3116
- function renderStreamMessage(message) {
3117
- session.renderStreamMessage(message);
3118
- }
3119
-
3120
- function clearCache() {
3121
- session.clearCache();
3122
- }
3123
-
3124
- function setProgressBarDelay(delay) {
3125
- session.setProgressBarDelay(delay);
3126
- }
3127
-
3128
- var Turbo = Object.freeze({
3129
- __proto__: null,
3130
- navigator: navigator,
3131
- PageRenderer: PageRenderer,
3132
- PageSnapshot: PageSnapshot,
3133
- start: start,
3134
- registerAdapter: registerAdapter,
3135
- visit: visit,
3136
- connectStreamSource: connectStreamSource,
3137
- disconnectStreamSource: disconnectStreamSource,
3138
- renderStreamMessage: renderStreamMessage,
3139
- clearCache: clearCache,
3140
- setProgressBarDelay: setProgressBarDelay
3141
- });
3142
-
3143
3223
  window.Turbo = Turbo;
3144
3224
 
3145
3225
  start();
@@ -3154,6 +3234,7 @@ var turbo_es2017Esm = Object.freeze({
3154
3234
  navigator: navigator,
3155
3235
  registerAdapter: registerAdapter,
3156
3236
  renderStreamMessage: renderStreamMessage,
3237
+ session: session,
3157
3238
  setProgressBarDelay: setProgressBarDelay,
3158
3239
  start: start,
3159
3240
  visit: visit
@@ -12,10 +12,9 @@ module Turbo::Streams::ActionHelper
12
12
  def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil)
13
13
  template = action.to_sym == :remove ? "" : "<template>#{template}</template>"
14
14
 
15
- if target
16
- target = convert_to_turbo_stream_dom_id(target)
15
+ if target = convert_to_turbo_stream_dom_id(target)
17
16
  %(<turbo-stream action="#{action}" target="#{target}">#{template}</turbo-stream>).html_safe
18
- elsif targets
17
+ elsif targets = convert_to_turbo_stream_dom_id(targets)
19
18
  %(<turbo-stream action="#{action}" targets="#{targets}">#{template}</turbo-stream>).html_safe
20
19
  else
21
20
  raise ArgumentError, "target or targets must be supplied"
@@ -212,29 +212,19 @@ class Turbo::Streams::TagBuilder
212
212
 
213
213
  # Send an action of the type <tt>name</tt> to <tt>target</tt>. Options described in the concrete methods.
214
214
  def action(name, target, content = nil, allow_inferred_rendering: true, **rendering, &block)
215
- target_name = extract_target_name_from(target)
216
215
  template = render_template(target, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
217
216
 
218
- turbo_stream_action_tag name, target: target_name, template: template
217
+ turbo_stream_action_tag name, target: target, template: template
219
218
  end
220
219
 
221
220
  # Send an action of the type <tt>name</tt> to <tt>targets</tt>. Options described in the concrete methods.
222
221
  def action_all(name, targets, content = nil, allow_inferred_rendering: true, **rendering, &block)
223
- targets_name = extract_target_name_from(targets)
224
222
  template = render_template(targets, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
225
223
 
226
- turbo_stream_action_tag name, targets: targets_name, template: template
224
+ turbo_stream_action_tag name, targets: targets, template: template
227
225
  end
228
226
 
229
227
  private
230
- def extract_target_name_from(target)
231
- if target.respond_to?(:to_key)
232
- ActionView::RecordIdentifier.dom_id(target)
233
- else
234
- target
235
- end
236
- end
237
-
238
228
  def render_template(target, content = nil, allow_inferred_rendering: true, **rendering, &block)
239
229
  case
240
230
  when content
@@ -0,0 +1,9 @@
1
+ if (cable_config_path = Rails.root.join("config/cable.yml")).exist?
2
+ say "Enable redis in bundle"
3
+ uncomment_lines "Gemfile", %(gem 'redis')
4
+
5
+ say "Switch development cable to use redis"
6
+ gsub_file cable_config_path.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
7
+ else
8
+ say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
9
+ end
@@ -1,15 +1,23 @@
1
- APP_JS_ROOT = Rails.root.join("app/assets/javascripts")
2
- CABLE_CONFIG_PATH = Rails.root.join("config/cable.yml")
3
-
4
- say "Import turbo-rails in existing app/assets/javascripts/application.js"
5
- append_to_file APP_JS_ROOT.join("application.js"), %(import "@hotwired/turbo-rails"\n)
6
-
7
- if CABLE_CONFIG_PATH.exist?
8
- say "Enable redis in bundle"
9
- uncomment_lines "Gemfile", %(gem 'redis')
1
+ if (app_js_path = Rails.root.join("app/javascript/application.js")).exist?
2
+ say "Import turbo-rails in existing app/javascript/application.js"
3
+ append_to_file app_js_path, %(import "@hotwired/turbo-rails"\n)
4
+ else
5
+ say <<~INSTRUCTIONS, :red
6
+ You must import @hotwired/turbo-rails in your application.js.
7
+ INSTRUCTIONS
8
+ end
10
9
 
11
- say "Switch development cable to use redis"
12
- gsub_file CABLE_CONFIG_PATH.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
10
+ if (importmap_path = Rails.root.join("config/importmap.rb")).exist?
11
+ say "Pin @hotwired/turbo-rails in config/importmap.rb"
12
+ insert_into_file \
13
+ importmap_path.to_s,
14
+ %( pin "@hotwired/turbo-rails", to: "turbo.js"\n\n),
15
+ after: "Rails.application.config.importmap.draw do\n"
13
16
  else
14
- say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
17
+ say <<~INSTRUCTIONS, :red
18
+ You must add @hotwired/turbo-rails to your importmap to reference them via ESM.
19
+ Example: pin "@hotwired/turbo-rails", to: "turbo.js"
20
+ INSTRUCTIONS
15
21
  end
22
+
23
+ say "Run turbo:install:redis to switch on Redis and use it in development for turbo streams", :red
@@ -1,27 +1,7 @@
1
- # Some Rails versions use commonJS(require) others use ESM(import).
2
- TURBOLINKS_REGEX = /(import .* from "turbolinks".*\n|require\("turbolinks"\).*\n)/.freeze
3
- ACTIVE_STORAGE_REGEX = /(import.*ActiveStorage|require.*@rails\/activestorage.*)/.freeze
4
- CABLE_CONFIG_PATH = Rails.root.join("config/cable.yml")
5
-
6
- abort "❌ Webpacker not found. Exiting." unless defined?(Webpacker::Engine)
1
+ say "Appending Turbo setup code to #{Webpacker.config.source_entry_path}/application.js"
2
+ append_to_file "#{Webpacker.config.source_entry_path}/application.js", %(\nimport "@hotwired/turbo-rails"\n)
7
3
 
8
4
  say "Install Turbo"
9
5
  run "yarn add @hotwired/turbo-rails"
10
- insert_into_file "#{Webpacker.config.source_entry_path}/application.js", "import \"@hotwired/turbo-rails\"\n", before: ACTIVE_STORAGE_REGEX
11
-
12
- say "Remove Turbolinks"
13
- run "#{RbConfig.ruby} bin/bundle remove turbolinks"
14
- run "#{RbConfig.ruby} bin/bundle", capture: true
15
- run "#{RbConfig.ruby} bin/yarn remove turbolinks"
16
- gsub_file "#{Webpacker.config.source_entry_path}/application.js", TURBOLINKS_REGEX, ''
17
- gsub_file "#{Webpacker.config.source_entry_path}/application.js", /Turbolinks.start.*\n/, ''
18
-
19
- if CABLE_CONFIG_PATH.exist?
20
- say "Enable redis in bundle"
21
- uncomment_lines "Gemfile", %(gem 'redis')
22
6
 
23
- say "Switch development cable to use redis"
24
- gsub_file CABLE_CONFIG_PATH.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
25
- else
26
- say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
27
- end
7
+ say "Run turbo:install:redis to switch on Redis and use it in development for turbo streams"
@@ -20,5 +20,10 @@ namespace :turbo do
20
20
  task :webpacker do
21
21
  run_turbo_install_template "turbo_with_webpacker"
22
22
  end
23
+
24
+ desc "Switch on Redis and use it in development"
25
+ task :redis do
26
+ run_turbo_install_template "turbo_needs_redis"
27
+ end
23
28
  end
24
29
  end
data/lib/turbo/engine.rb CHANGED
@@ -26,14 +26,6 @@ module Turbo
26
26
  end
27
27
  end
28
28
 
29
- initializer "turbo.importmap" do
30
- if Rails.application.config.respond_to?(:importmap)
31
- Rails.application.config.importmap.paths.tap do |paths|
32
- paths.asset "@hotwired/turbo-rails", path: "turbo.js"
33
- end
34
- end
35
- end
36
-
37
29
  initializer "turbo.helpers", before: :load_config_initializers do
38
30
  ActiveSupport.on_load(:action_controller_base) do
39
31
  include Turbo::Streams::TurboStreamsTagBuilder, Turbo::Frames::FrameRequest, Turbo::Native::Navigation
data/lib/turbo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Turbo
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-08-13 00:00:00.000000000 Z
13
+ date: 2021-08-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -56,6 +56,7 @@ files:
56
56
  - app/models/concerns/turbo/broadcastable.rb
57
57
  - app/models/turbo/streams/tag_builder.rb
58
58
  - config/routes.rb
59
+ - lib/install/turbo_needs_redis.rb
59
60
  - lib/install/turbo_with_asset_pipeline.rb
60
61
  - lib/install/turbo_with_webpacker.rb
61
62
  - lib/tasks/turbo_tasks.rake