turbo-mount 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +7 -2
- data/app/assets/javascripts/turbo-mount/svelte.js +3 -2
- data/app/assets/javascripts/turbo-mount/svelte.min.js +1 -1
- data/app/assets/javascripts/turbo-mount/svelte.min.js.map +1 -1
- data/app/assets/javascripts/turbo-mount/svelte4.js +15 -0
- data/app/assets/javascripts/turbo-mount/svelte4.min.js +2 -0
- data/app/assets/javascripts/turbo-mount/svelte4.min.js.map +1 -0
- data/app/assets/javascripts/turbo-mount.js +3 -5
- data/app/assets/javascripts/turbo-mount.min.js +1 -1
- data/app/assets/javascripts/turbo-mount.min.js.map +1 -1
- data/lib/generators/turbo_mount/frameworks.yml +23 -0
- data/lib/generators/turbo_mount/helpers.rb +61 -0
- data/lib/generators/turbo_mount/install/turbo-mount.js.tt +1 -1
- data/lib/generators/turbo_mount/install_generator.rb +33 -70
- data/lib/generators/turbo_mount/js_package_manager.rb +57 -0
- data/lib/turbo/mount/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1e390ef4fc7a3d329693a294437fa8f5d27c9157654b3a9b21621b26dd6c7c5
|
4
|
+
data.tar.gz: 591ea18f8b02da05da9a9d6dba5249eccdf01ec2b4d00477341df76166c0bd7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd306af35603685bebc4c2a7ef0fe31909fa56b521f0808562aeee3f8c90f2d5098aa846e6633356b716ced5aa51b8c5e6131252b97c5b91ccf14242aa90baff
|
7
|
+
data.tar.gz: 8a7221dad2c96c5c8c873e841a4007f1b45bcf6adada668af008692b035589590a230c411756f1cac86918d74521d0f6cec47d8c2125feddb1b5bfca91187ec3
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning].
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.4.0] - 2024-11-03
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- [BREAKING] Support Svelte 5. ([@skryukov])
|
15
|
+
`turbo-mount/svelte` is now the Svelte 5 plugin. The Svelte 4 plugin is now `turbo-mount/svelte4`.
|
16
|
+
|
10
17
|
## [0.3.3] - 2024-09-24
|
11
18
|
|
12
19
|
### Added
|
@@ -75,7 +82,8 @@ and this project adheres to [Semantic Versioning].
|
|
75
82
|
[@jkogara]: https://github.com/jkogara
|
76
83
|
[@skryukov]: https://github.com/skryukov
|
77
84
|
|
78
|
-
[Unreleased]: https://github.com/skryukov/turbo-mount/compare/v0.
|
85
|
+
[Unreleased]: https://github.com/skryukov/turbo-mount/compare/v0.4.0...HEAD
|
86
|
+
[0.4.0]: https://github.com/skryukov/turbo-mount/compare/v0.3.3...v0.4.0
|
79
87
|
[0.3.3]: https://github.com/skryukov/turbo-mount/compare/v0.3.2...v0.3.3
|
80
88
|
[0.3.2]: https://github.com/skryukov/turbo-mount/compare/v0.3.1...v0.3.2
|
81
89
|
[0.3.1]: https://github.com/skryukov/turbo-mount/compare/v0.3.0...v0.3.1
|
data/README.md
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
<p align="center">
|
2
|
+
<img src="./assets/logo.svg" title="Turbo Mount logo" width="220" height="45">
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<h1 align="center">Turbo Mount</h1>
|
2
6
|
|
3
7
|
[![Gem Version](https://badge.fury.io/rb/turbo-mount.svg)](https://rubygems.org/gems/turbo-mount)
|
4
8
|
|
@@ -143,7 +147,8 @@ This will generate the following HTML:
|
|
143
147
|
|
144
148
|
- React: `"turbo-mount/react"`
|
145
149
|
- Vue: `"turbo-mount/vue"`
|
146
|
-
- Svelte: `"turbo-mount/
|
150
|
+
- Svelte 4: `"turbo-mount/svelte4"`
|
151
|
+
- Svelte 5: `"turbo-mount/svelte"`
|
147
152
|
|
148
153
|
To add support for other frameworks, create a custom plugin. See included plugins for examples.
|
149
154
|
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import { buildRegisterFunction } from 'turbo-mount';
|
2
2
|
export { TurboMount } from 'turbo-mount';
|
3
|
+
import { mount, unmount } from 'svelte';
|
3
4
|
|
4
5
|
const plugin = {
|
5
6
|
mountComponent: (mountProps) => {
|
6
7
|
const { el, Component, props } = mountProps;
|
7
|
-
const component =
|
8
|
+
const component = mount(Component, { target: el, props });
|
8
9
|
return () => {
|
9
|
-
component
|
10
|
+
unmount(component);
|
10
11
|
};
|
11
12
|
},
|
12
13
|
};
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{buildRegisterFunction as o}from"turbo-mount";export{TurboMount}from"turbo-mount";const
|
1
|
+
import{buildRegisterFunction as o}from"turbo-mount";export{TurboMount}from"turbo-mount";import{mount as t,unmount as r}from"svelte";const n={mountComponent:o=>{const{el:n,Component:e,props:m}=o,p=t(e,{target:n,props:m});return()=>{r(p)}}},e=o(n);export{n as default,e as registerComponent};
|
2
2
|
//# sourceMappingURL=svelte.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"svelte.min.js","sources":["../../src/plugins/svelte/index.ts"],"sourcesContent":["import { buildRegisterFunction, Plugin, TurboMount } from \"turbo-mount\";\nimport {
|
1
|
+
{"version":3,"file":"svelte.min.js","sources":["../../src/plugins/svelte/index.ts"],"sourcesContent":["import { buildRegisterFunction, Plugin, TurboMount } from \"turbo-mount\";\nimport { Component, mount, unmount } from \"svelte\";\n\nconst plugin: Plugin<Component> = {\n mountComponent: (mountProps) => {\n const { el, Component, props } = mountProps;\n const component = mount(Component, { target: el, props });\n return () => {\n unmount(component);\n };\n },\n};\n\nconst registerComponent = buildRegisterFunction(plugin);\n\nexport { TurboMount, registerComponent };\n\nexport default plugin;\n"],"names":["plugin","mountComponent","mountProps","el","Component","props","component","mount","target","unmount","registerComponent","buildRegisterFunction"],"mappings":"oIAGA,MAAMA,EAA4B,CAChCC,eAAiBC,IACf,MAAMC,GAAEA,EAAEC,UAAEA,EAASC,MAAEA,GAAUH,EAC3BI,EAAYC,EAAMH,EAAW,CAAEI,OAAQL,EAAIE,UACjD,MAAO,KACLI,EAAQH,EAAU,CACnB,GAICI,EAAoBC,EAAsBX"}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
3
|
+
|
4
|
+
const plugin = {
|
5
|
+
mountComponent: (mountProps) => {
|
6
|
+
const { el, Component, props } = mountProps;
|
7
|
+
const component = new Component({ target: el, props });
|
8
|
+
return () => {
|
9
|
+
component.$destroy();
|
10
|
+
};
|
11
|
+
},
|
12
|
+
};
|
13
|
+
const registerComponent = buildRegisterFunction(plugin);
|
14
|
+
|
15
|
+
export { plugin as default, registerComponent };
|
@@ -0,0 +1,2 @@
|
|
1
|
+
import{buildRegisterFunction as o}from"turbo-mount";export{TurboMount}from"turbo-mount";const t={mountComponent:o=>{const{el:t,Component:r,props:n}=o,e=new r({target:t,props:n});return()=>{e.$destroy()}}},r=o(t);export{t as default,r as registerComponent};
|
2
|
+
//# sourceMappingURL=svelte4.min.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"svelte4.min.js","sources":["../../src/plugins/svelte4/index.ts"],"sourcesContent":["import { buildRegisterFunction, Plugin, TurboMount } from \"turbo-mount\";\nimport { ComponentType } from \"svelte\";\n\nconst plugin: Plugin<ComponentType> = {\n mountComponent: (mountProps) => {\n const { el, Component, props } = mountProps;\n\n const component = new Component({ target: el, props });\n\n return () => {\n component.$destroy();\n };\n },\n};\n\nconst registerComponent = buildRegisterFunction(plugin);\n\nexport { TurboMount, registerComponent };\n\nexport default plugin;\n"],"names":["plugin","mountComponent","mountProps","el","Component","props","component","target","$destroy","registerComponent","buildRegisterFunction"],"mappings":"wFAGA,MAAMA,EAAgC,CACpCC,eAAiBC,IACf,MAAMC,GAAEA,EAAEC,UAAEA,EAASC,MAAEA,GAAUH,EAE3BI,EAAY,IAAIF,EAAU,CAAEG,OAAQJ,EAAIE,UAE9C,MAAO,KACLC,EAAUE,UAAU,CACrB,GAICC,EAAoBC,EAAsBV"}
|
@@ -64,10 +64,9 @@ class TurboMount {
|
|
64
64
|
this.application.turboMount = this;
|
65
65
|
this.application.register("turbo-mount", TurboMountController);
|
66
66
|
document.addEventListener("turbo:before-morph-element", (event) => {
|
67
|
-
var _a;
|
68
67
|
const turboMorphEvent = event;
|
69
68
|
const { target, detail } = turboMorphEvent;
|
70
|
-
if (
|
69
|
+
if (target.getAttribute("data-controller")?.includes("turbo-mount")) {
|
71
70
|
target.setAttribute("data-turbo-mount-props-value", detail.newElement.getAttribute("data-turbo-mount-props-value") ||
|
72
71
|
"{}");
|
73
72
|
event.preventDefault();
|
@@ -112,15 +111,14 @@ const identifierNames = (name) => {
|
|
112
111
|
return [`turbo-mount--${controllerName}`, `turbo-mount-${controllerName}`];
|
113
112
|
};
|
114
113
|
const registerComponentsBase = ({ plugin, turboMount, components, controllers, }) => {
|
115
|
-
|
116
|
-
const controllerModules = controllers !== null && controllers !== void 0 ? controllers : [];
|
114
|
+
const controllerModules = controllers ?? [];
|
117
115
|
for (const { module, filename } of components) {
|
118
116
|
const name = filename
|
119
117
|
.replace(/\.\w*$/, "")
|
120
118
|
.replace(/^[./]*components\//, "");
|
121
119
|
const identifiers = identifierNames(name);
|
122
120
|
const controller = controllerModules.find(({ identifier }) => identifiers.includes(identifier));
|
123
|
-
const component =
|
121
|
+
const component = module.default ?? module;
|
124
122
|
if (controller) {
|
125
123
|
turboMount.register(plugin, name, component, controller.controllerConstructor);
|
126
124
|
}
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{Controller as t,Application as o}from"@hotwired/stimulus";class n extends t{constructor(){super(...arguments),this.skipPropsChangeCallback=!1}connect(){this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps))}disconnect(){this.umountComponent()}propsValueChanged(){this.skipPropsChangeCallback?this.skipPropsChangeCallback=!1:(this.umountComponent(),this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps)))}get componentProps(){return this.propsValue}get mountElement(){return this.hasMountTarget?this.mountTarget:this.element}get resolvedComponent(){return this.resolveMounted(this.componentValue).component}get resolvedPlugin(){return this.resolveMounted(this.componentValue).plugin}umountComponent(){this._umountComponentCallback&&this._umountComponentCallback(),this._umountComponentCallback=void 0}mountComponent(t,o,n){return this.resolvedPlugin.mountComponent({el:t,Component:o,props:n})}resolveMounted(t){return this.application.turboMount.resolve(t)}setComponentProps(t){this.skipPropsChangeCallback=!0,this.propsValue=t}}n.values={props:Object,component:String},n.targets=["mount"];const e=t=>t.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();class r{constructor(t={}){this.components=new Map,this.application=this.findOrStartApplication(t.application),this.application.turboMount=this,this.application.register("turbo-mount",n),document.addEventListener("turbo:before-morph-element",(t=>{
|
1
|
+
import{Controller as t,Application as o}from"@hotwired/stimulus";class n extends t{constructor(){super(...arguments),this.skipPropsChangeCallback=!1}connect(){this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps))}disconnect(){this.umountComponent()}propsValueChanged(){this.skipPropsChangeCallback?this.skipPropsChangeCallback=!1:(this.umountComponent(),this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps)))}get componentProps(){return this.propsValue}get mountElement(){return this.hasMountTarget?this.mountTarget:this.element}get resolvedComponent(){return this.resolveMounted(this.componentValue).component}get resolvedPlugin(){return this.resolveMounted(this.componentValue).plugin}umountComponent(){this._umountComponentCallback&&this._umountComponentCallback(),this._umountComponentCallback=void 0}mountComponent(t,o,n){return this.resolvedPlugin.mountComponent({el:t,Component:o,props:n})}resolveMounted(t){return this.application.turboMount.resolve(t)}setComponentProps(t){this.skipPropsChangeCallback=!0,this.propsValue=t}}n.values={props:Object,component:String},n.targets=["mount"];const e=t=>t.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();class r{constructor(t={}){this.components=new Map,this.application=this.findOrStartApplication(t.application),this.application.turboMount=this,this.application.register("turbo-mount",n),document.addEventListener("turbo:before-morph-element",(t=>{const o=t,{target:n,detail:e}=o;n.getAttribute("data-controller")?.includes("turbo-mount")&&(n.setAttribute("data-turbo-mount-props-value",e.newElement.getAttribute("data-turbo-mount-props-value")||"{}"),t.preventDefault())}))}register(t,o,r,s){if(s||(s=n),this.components.has(o))throw new Error(`Component '${o}' is already registered.`);if(this.components.set(o,{component:r,plugin:t}),s){const t=`turbo-mount-${e(o).replace("/","--")}`;this.application.register(t,s)}}resolve(t){const o=this.components.get(t);if(!o)throw new Error(`Unknown component: ${t}`);return o}findOrStartApplication(t){let n=t||window.Stimulus;return n||(n=o.start(),window.Stimulus=n),n}}function s(t){return(o,n,e,r)=>{o.register(t,n,e,r)}}const i=t=>{const o=e(t).replace("/","--");return[`turbo-mount--${o}`,`turbo-mount-${o}`]},u=({plugin:t,turboMount:o,components:n,controllers:e})=>{const r=e??[];for(const{module:e,filename:s}of n){const n=s.replace(/\.\w*$/,"").replace(/^[./]*components\//,""),u=i(n),p=r.find((({identifier:t})=>u.includes(t))),l=e.default??e;p?o.register(t,n,l,p.controllerConstructor):o.register(t,n,l)}};export{r as TurboMount,n as TurboMountController,s as buildRegisterFunction,u as registerComponentsBase};
|
2
2
|
//# sourceMappingURL=turbo-mount.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"turbo-mount.min.js","sources":["../src/turbo-mount-controller.ts","../src/helpers.ts","../src/turbo-mount.ts","../src/registerComponentsBase.ts"],"sourcesContent":["import { Controller } from \"@hotwired/stimulus\";\nimport { ApplicationWithTurboMount } from \"./turbo-mount\";\n\nexport class TurboMountController extends Controller {\n static values = {\n props: Object,\n component: String,\n };\n static targets = [\"mount\"];\n\n private skipPropsChangeCallback = false;\n\n declare propsValue: object;\n declare componentValue: string;\n declare readonly hasMountTarget: boolean;\n declare readonly mountTarget: Element;\n\n _umountComponentCallback?: () => void;\n\n connect() {\n this._umountComponentCallback ||= this.mountComponent(\n this.mountElement,\n this.resolvedComponent,\n this.componentProps,\n );\n }\n\n disconnect() {\n this.umountComponent();\n }\n\n propsValueChanged() {\n // Prevent re-mounting the component if the props are being set by the component itself\n if (this.skipPropsChangeCallback) {\n this.skipPropsChangeCallback = false;\n return;\n }\n\n this.umountComponent();\n this._umountComponentCallback ||= this.mountComponent(\n this.mountElement,\n this.resolvedComponent,\n this.componentProps,\n );\n }\n\n get componentProps() {\n return this.propsValue;\n }\n\n get mountElement() {\n return this.hasMountTarget ? this.mountTarget : this.element;\n }\n\n get resolvedComponent() {\n return this.resolveMounted(this.componentValue).component;\n }\n\n get resolvedPlugin() {\n return this.resolveMounted(this.componentValue).plugin;\n }\n\n umountComponent() {\n this._umountComponentCallback && this._umountComponentCallback();\n this._umountComponentCallback = undefined;\n }\n\n mountComponent(el: Element, Component: unknown, props: object) {\n return this.resolvedPlugin.mountComponent({ el, Component, props });\n }\n\n resolveMounted(component: string) {\n const app = this.application as ApplicationWithTurboMount;\n return app.turboMount.resolve(component);\n }\n\n setComponentProps(props: object) {\n this.skipPropsChangeCallback = true;\n this.propsValue = props;\n }\n}\n","export const camelToKebabCase = (str: string) => {\n return str.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n};\n","import { Application, ControllerConstructor } from \"@hotwired/stimulus\";\n\nimport { camelToKebabCase } from \"./helpers\";\nimport { TurboMountController } from \"./turbo-mount-controller\";\n\ndeclare global {\n interface Window {\n Stimulus?: Application;\n }\n}\n\nexport interface ApplicationWithTurboMount extends Application {\n turboMount: TurboMount;\n}\n\nexport type MountComponentProps<T> = {\n el: Element;\n Component: T;\n props: object;\n};\n\nexport type Plugin<T> = {\n mountComponent: (props: MountComponentProps<T>) => () => void;\n};\n\nexport type TurboMountProps = {\n application?: Application;\n};\n\ntype TurboMountComponents<T> = Map<string, { component: T; plugin: Plugin<T> }>;\n\ninterface TurboMorphEvent extends CustomEvent {\n target: Element;\n detail: {\n newElement: Element;\n };\n}\n\nexport class TurboMount {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components: TurboMountComponents<any>;\n application: ApplicationWithTurboMount;\n\n constructor(props: TurboMountProps = {}) {\n this.components = new Map();\n this.application = this.findOrStartApplication(props.application);\n this.application.turboMount = this;\n this.application.register(\"turbo-mount\", TurboMountController);\n\n document.addEventListener(\"turbo:before-morph-element\", (event) => {\n const turboMorphEvent = event as unknown as TurboMorphEvent;\n const { target, detail } = turboMorphEvent;\n\n if (target.getAttribute(\"data-controller\")?.includes(\"turbo-mount\")) {\n target.setAttribute(\n \"data-turbo-mount-props-value\",\n detail.newElement.getAttribute(\"data-turbo-mount-props-value\") ||\n \"{}\",\n );\n event.preventDefault();\n }\n });\n }\n\n register<T>(\n plugin: Plugin<T>,\n name: string,\n component: T,\n controller?: ControllerConstructor,\n ) {\n controller ||= TurboMountController;\n if (this.components.has(name)) {\n throw new Error(`Component '${name}' is already registered.`);\n }\n this.components.set(name, { component, plugin });\n\n if (controller) {\n const controllerName = `turbo-mount-${camelToKebabCase(name).replace(\"/\", \"--\")}`;\n this.application.register(controllerName, controller);\n }\n }\n\n resolve(name: string) {\n const component = this.components.get(name);\n if (!component) {\n throw new Error(`Unknown component: ${name}`);\n }\n return component;\n }\n\n private findOrStartApplication(hydratedApp?: Application) {\n let application = hydratedApp || window.Stimulus;\n\n if (!application) {\n application = Application.start();\n window.Stimulus = application;\n }\n return application as ApplicationWithTurboMount;\n }\n}\n\nexport function buildRegisterFunction<T>(plugin: Plugin<T>) {\n return (\n turboMount: TurboMount,\n name: string,\n component: T,\n controller?: ControllerConstructor,\n ) => {\n turboMount.register(plugin, name, component, controller);\n };\n}\n","import { Definition } from \"@hotwired/stimulus\";\n\nimport { TurboMount, Plugin } from \"./turbo-mount\";\nimport { camelToKebabCase } from \"./helpers\";\n\nexport type ComponentModule = { default: never } | never;\n\nexport type ComponentDefinition = {\n filename: string;\n module: ComponentModule;\n};\n\ntype RegisterComponentsProps<T> = {\n plugin: Plugin<T>;\n turboMount: TurboMount;\n components: ComponentDefinition[];\n controllers?: Definition[];\n};\n\nconst identifierNames = (name: string) => {\n const controllerName = camelToKebabCase(name).replace(\"/\", \"--\");\n\n return [`turbo-mount--${controllerName}`, `turbo-mount-${controllerName}`];\n};\n\nexport const registerComponentsBase = <T>({\n plugin,\n turboMount,\n components,\n controllers,\n}: RegisterComponentsProps<T>) => {\n const controllerModules = controllers ?? [];\n\n for (const { module, filename } of components) {\n const name = filename\n .replace(/\\.\\w*$/, \"\")\n .replace(/^[./]*components\\//, \"\");\n\n const identifiers = identifierNames(name);\n\n const controller = controllerModules.find(({ identifier }) =>\n identifiers.includes(identifier),\n );\n const component = module.default ?? module;\n\n if (controller) {\n turboMount.register(\n plugin,\n name,\n component,\n controller.controllerConstructor,\n );\n } else {\n turboMount.register(plugin, name, component);\n }\n }\n};\n"],"names":["TurboMountController","Controller","constructor","this","skipPropsChangeCallback","connect","_umountComponentCallback","mountComponent","mountElement","resolvedComponent","componentProps","disconnect","umountComponent","propsValueChanged","propsValue","hasMountTarget","mountTarget","element","resolveMounted","componentValue","component","resolvedPlugin","plugin","undefined","el","Component","props","application","turboMount","resolve","setComponentProps","values","Object","String","targets","camelToKebabCase","str","replace","toLowerCase","TurboMount","components","Map","findOrStartApplication","register","document","addEventListener","event","turboMorphEvent","target","detail","_a","getAttribute","includes","setAttribute","newElement","preventDefault","name","controller","has","Error","set","controllerName","get","hydratedApp","window","Stimulus","Application","start","buildRegisterFunction","identifierNames","registerComponentsBase","controllers","controllerModules","module","filename","identifiers","find","identifier","default","controllerConstructor"],"mappings":"iEAGM,MAAOA,UAA6BC,EAA1C,WAAAC,uBAOUC,KAAuBC,yBAAG,CAsEnC,CA7DC,OAAAC,GACEF,KAAKG,2BAALH,KAAKG,yBAA6BH,KAAKI,eACrCJ,KAAKK,aACLL,KAAKM,kBACLN,KAAKO,gBAER,CAED,UAAAC,GACER,KAAKS,iBACN,CAED,iBAAAC,GAEMV,KAAKC,wBACPD,KAAKC,yBAA0B,GAIjCD,KAAKS,kBACLT,KAAKG,2BAALH,KAAKG,yBAA6BH,KAAKI,eACrCJ,KAAKK,aACLL,KAAKM,kBACLN,KAAKO,iBAER,CAED,kBAAIA,GACF,OAAOP,KAAKW,UACb,CAED,gBAAIN,GACF,OAAOL,KAAKY,eAAiBZ,KAAKa,YAAcb,KAAKc,OACtD,CAED,qBAAIR,GACF,OAAON,KAAKe,eAAef,KAAKgB,gBAAgBC,SACjD,CAED,kBAAIC,GACF,OAAOlB,KAAKe,eAAef,KAAKgB,gBAAgBG,MACjD,CAED,eAAAV,GACET,KAAKG,0BAA4BH,KAAKG,2BACtCH,KAAKG,8BAA2BiB,CACjC,CAED,cAAAhB,CAAeiB,EAAaC,EAAoBC,GAC9C,OAAOvB,KAAKkB,eAAed,eAAe,CAAEiB,KAAIC,YAAWC,SAC5D,CAED,cAAAR,CAAeE,GAEb,OADYjB,KAAKwB,YACNC,WAAWC,QAAQT,EAC/B,CAED,iBAAAU,CAAkBJ,GAChBvB,KAAKC,yBAA0B,EAC/BD,KAAKW,WAAaY,CACnB,EA3EM1B,EAAA+B,OAAS,CACdL,MAAOM,OACPZ,UAAWa,QAENjC,EAAAkC,QAAU,CAAC,SCRb,MAAMC,EAAoBC,GACxBA,EAAIC,QAAQ,kBAAmB,SAASC,oBCqCpCC,EAKX,WAAArC,CAAYwB,EAAyB,IACnCvB,KAAKqC,WAAa,IAAIC,IACtBtC,KAAKwB,YAAcxB,KAAKuC,uBAAuBhB,EAAMC,aACrDxB,KAAKwB,YAAYC,WAAazB,KAC9BA,KAAKwB,YAAYgB,SAAS,cAAe3C,GAEzC4C,SAASC,iBAAiB,8BAA+BC,UACvD,MAAMC,EAAkBD,GAClBE,OAAEA,EAAMC,OAAEA,GAAWF,GAEe,QAAtCG,EAAAF,EAAOG,aAAa,0BAAkB,IAAAD,OAAA,EAAAA,EAAEE,SAAS,kBACnDJ,EAAOK,aACL,+BACAJ,EAAOK,WAAWH,aAAa,iCAC7B,MAEJL,EAAMS,iBACP,GAEJ,CAED,QAAAZ,CACErB,EACAkC,EACApC,EACAqC,GAGA,GADAA,IAAAA,EAAezD,GACXG,KAAKqC,WAAWkB,IAAIF,GACtB,MAAM,IAAIG,MAAM,cAAcH,6BAIhC,GAFArD,KAAKqC,WAAWoB,IAAIJ,EAAM,CAAEpC,YAAWE,WAEnCmC,EAAY,CACd,MAAMI,EAAiB,eAAe1B,EAAiBqB,GAAMnB,QAAQ,IAAK,QAC1ElC,KAAKwB,YAAYgB,SAASkB,EAAgBJ,EAC3C,CACF,CAED,OAAA5B,CAAQ2B,GACN,MAAMpC,EAAYjB,KAAKqC,WAAWsB,IAAIN,GACtC,IAAKpC,EACH,MAAM,IAAIuC,MAAM,sBAAsBH,KAExC,OAAOpC,CACR,CAEO,sBAAAsB,CAAuBqB,GAC7B,IAAIpC,EAAcoC,GAAeC,OAAOC,SAMxC,OAJKtC,IACHA,EAAcuC,EAAYC,QAC1BH,OAAOC,SAAWtC,GAEbA,CACR,EAGG,SAAUyC,EAAyB9C,GACvC,MAAO,CACLM,EACA4B,EACApC,EACAqC,KAEA7B,EAAWe,SAASrB,EAAQkC,EAAMpC,EAAWqC,EAAW,CAE5D,CC3FA,MAAMY,EAAmBb,IACvB,MAAMK,EAAiB1B,EAAiBqB,GAAMnB,QAAQ,IAAK,MAE3D,MAAO,CAAC,gBAAgBwB,IAAkB,eAAeA,IAAiB,EAG/DS,EAAyB,EACpChD,SACAM,aACAY,aACA+B,wBAEA,MAAMC,EAAoBD,QAAAA,EAAe,GAEzC,IAAK,MAAME,OAAEA,EAAMC,SAAEA,KAAclC,EAAY,CAC7C,MAAMgB,EAAOkB,EACVrC,QAAQ,SAAU,IAClBA,QAAQ,qBAAsB,IAE3BsC,EAAcN,EAAgBb,GAE9BC,EAAae,EAAkBI,MAAK,EAAGC,gBAC3CF,EAAYvB,SAASyB,KAEjBzD,EAA0B,QAAd8B,EAAAuB,EAAOK,eAAO,IAAA5B,EAAAA,EAAIuB,EAEhChB,EACF7B,EAAWe,SACTrB,EACAkC,EACApC,EACAqC,EAAWsB,uBAGbnD,EAAWe,SAASrB,EAAQkC,EAAMpC,EAErC"}
|
1
|
+
{"version":3,"file":"turbo-mount.min.js","sources":["../src/turbo-mount-controller.ts","../src/helpers.ts","../src/turbo-mount.ts","../src/registerComponentsBase.ts"],"sourcesContent":["import { Controller } from \"@hotwired/stimulus\";\nimport { ApplicationWithTurboMount } from \"./turbo-mount\";\n\nexport class TurboMountController extends Controller {\n static values = {\n props: Object,\n component: String,\n };\n static targets = [\"mount\"];\n\n private skipPropsChangeCallback = false;\n\n declare propsValue: object;\n declare componentValue: string;\n declare readonly hasMountTarget: boolean;\n declare readonly mountTarget: Element;\n\n _umountComponentCallback?: () => void;\n\n connect() {\n this._umountComponentCallback ||= this.mountComponent(\n this.mountElement,\n this.resolvedComponent,\n this.componentProps,\n );\n }\n\n disconnect() {\n this.umountComponent();\n }\n\n propsValueChanged() {\n // Prevent re-mounting the component if the props are being set by the component itself\n if (this.skipPropsChangeCallback) {\n this.skipPropsChangeCallback = false;\n return;\n }\n\n this.umountComponent();\n this._umountComponentCallback ||= this.mountComponent(\n this.mountElement,\n this.resolvedComponent,\n this.componentProps,\n );\n }\n\n get componentProps() {\n return this.propsValue;\n }\n\n get mountElement() {\n return this.hasMountTarget ? this.mountTarget : this.element;\n }\n\n get resolvedComponent() {\n return this.resolveMounted(this.componentValue).component;\n }\n\n get resolvedPlugin() {\n return this.resolveMounted(this.componentValue).plugin;\n }\n\n umountComponent() {\n this._umountComponentCallback && this._umountComponentCallback();\n this._umountComponentCallback = undefined;\n }\n\n mountComponent(el: Element, Component: unknown, props: object) {\n return this.resolvedPlugin.mountComponent({ el, Component, props });\n }\n\n resolveMounted(component: string) {\n const app = this.application as ApplicationWithTurboMount;\n return app.turboMount.resolve(component);\n }\n\n setComponentProps(props: object) {\n this.skipPropsChangeCallback = true;\n this.propsValue = props;\n }\n}\n","export const camelToKebabCase = (str: string) => {\n return str.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n};\n","import { Application, ControllerConstructor } from \"@hotwired/stimulus\";\n\nimport { camelToKebabCase } from \"./helpers\";\nimport { TurboMountController } from \"./turbo-mount-controller\";\n\ndeclare global {\n interface Window {\n Stimulus?: Application;\n }\n}\n\nexport interface ApplicationWithTurboMount extends Application {\n turboMount: TurboMount;\n}\n\nexport type MountComponentProps<T> = {\n el: Element;\n Component: T;\n props: object;\n};\n\nexport type Plugin<T> = {\n mountComponent: (props: MountComponentProps<T>) => () => void;\n};\n\nexport type TurboMountProps = {\n application?: Application;\n};\n\ntype TurboMountComponents<T> = Map<string, { component: T; plugin: Plugin<T> }>;\n\ninterface TurboMorphEvent extends CustomEvent {\n target: Element;\n detail: {\n newElement: Element;\n };\n}\n\nexport class TurboMount {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components: TurboMountComponents<any>;\n application: ApplicationWithTurboMount;\n\n constructor(props: TurboMountProps = {}) {\n this.components = new Map();\n this.application = this.findOrStartApplication(props.application);\n this.application.turboMount = this;\n this.application.register(\"turbo-mount\", TurboMountController);\n\n document.addEventListener(\"turbo:before-morph-element\", (event) => {\n const turboMorphEvent = event as unknown as TurboMorphEvent;\n const { target, detail } = turboMorphEvent;\n\n if (target.getAttribute(\"data-controller\")?.includes(\"turbo-mount\")) {\n target.setAttribute(\n \"data-turbo-mount-props-value\",\n detail.newElement.getAttribute(\"data-turbo-mount-props-value\") ||\n \"{}\",\n );\n event.preventDefault();\n }\n });\n }\n\n register<T>(\n plugin: Plugin<T>,\n name: string,\n component: T,\n controller?: ControllerConstructor,\n ) {\n controller ||= TurboMountController;\n if (this.components.has(name)) {\n throw new Error(`Component '${name}' is already registered.`);\n }\n this.components.set(name, { component, plugin });\n\n if (controller) {\n const controllerName = `turbo-mount-${camelToKebabCase(name).replace(\"/\", \"--\")}`;\n this.application.register(controllerName, controller);\n }\n }\n\n resolve(name: string) {\n const component = this.components.get(name);\n if (!component) {\n throw new Error(`Unknown component: ${name}`);\n }\n return component;\n }\n\n private findOrStartApplication(hydratedApp?: Application) {\n let application = hydratedApp || window.Stimulus;\n\n if (!application) {\n application = Application.start();\n window.Stimulus = application;\n }\n return application as ApplicationWithTurboMount;\n }\n}\n\nexport function buildRegisterFunction<T>(plugin: Plugin<T>) {\n return (\n turboMount: TurboMount,\n name: string,\n component: T,\n controller?: ControllerConstructor,\n ) => {\n turboMount.register(plugin, name, component, controller);\n };\n}\n","import { Definition } from \"@hotwired/stimulus\";\n\nimport { TurboMount, Plugin } from \"./turbo-mount\";\nimport { camelToKebabCase } from \"./helpers\";\n\nexport type ComponentModule = { default: never } | never;\n\nexport type ComponentDefinition = {\n filename: string;\n module: ComponentModule;\n};\n\ntype RegisterComponentsProps<T> = {\n plugin: Plugin<T>;\n turboMount: TurboMount;\n components: ComponentDefinition[];\n controllers?: Definition[];\n};\n\nconst identifierNames = (name: string) => {\n const controllerName = camelToKebabCase(name).replace(\"/\", \"--\");\n\n return [`turbo-mount--${controllerName}`, `turbo-mount-${controllerName}`];\n};\n\nexport const registerComponentsBase = <T>({\n plugin,\n turboMount,\n components,\n controllers,\n}: RegisterComponentsProps<T>) => {\n const controllerModules = controllers ?? [];\n\n for (const { module, filename } of components) {\n const name = filename\n .replace(/\\.\\w*$/, \"\")\n .replace(/^[./]*components\\//, \"\");\n\n const identifiers = identifierNames(name);\n\n const controller = controllerModules.find(({ identifier }) =>\n identifiers.includes(identifier),\n );\n const component = module.default ?? module;\n\n if (controller) {\n turboMount.register(\n plugin,\n name,\n component,\n controller.controllerConstructor,\n );\n } else {\n turboMount.register(plugin, name, component);\n }\n }\n};\n"],"names":["TurboMountController","Controller","constructor","this","skipPropsChangeCallback","connect","_umountComponentCallback","mountComponent","mountElement","resolvedComponent","componentProps","disconnect","umountComponent","propsValueChanged","propsValue","hasMountTarget","mountTarget","element","resolveMounted","componentValue","component","resolvedPlugin","plugin","undefined","el","Component","props","application","turboMount","resolve","setComponentProps","values","Object","String","targets","camelToKebabCase","str","replace","toLowerCase","TurboMount","components","Map","findOrStartApplication","register","document","addEventListener","event","turboMorphEvent","target","detail","getAttribute","includes","setAttribute","newElement","preventDefault","name","controller","has","Error","set","controllerName","get","hydratedApp","window","Stimulus","Application","start","buildRegisterFunction","identifierNames","registerComponentsBase","controllers","controllerModules","module","filename","identifiers","find","identifier","default","controllerConstructor"],"mappings":"iEAGM,MAAOA,UAA6BC,EAA1C,WAAAC,uBAOUC,KAAuBC,yBAAG,EASlC,OAAAC,GACEF,KAAKG,2BAALH,KAAKG,yBAA6BH,KAAKI,eACrCJ,KAAKK,aACLL,KAAKM,kBACLN,KAAKO,iBAIT,UAAAC,GACER,KAAKS,kBAGP,iBAAAC,GAEMV,KAAKC,wBACPD,KAAKC,yBAA0B,GAIjCD,KAAKS,kBACLT,KAAKG,2BAALH,KAAKG,yBAA6BH,KAAKI,eACrCJ,KAAKK,aACLL,KAAKM,kBACLN,KAAKO,kBAIT,kBAAIA,GACF,OAAOP,KAAKW,WAGd,gBAAIN,GACF,OAAOL,KAAKY,eAAiBZ,KAAKa,YAAcb,KAAKc,QAGvD,qBAAIR,GACF,OAAON,KAAKe,eAAef,KAAKgB,gBAAgBC,UAGlD,kBAAIC,GACF,OAAOlB,KAAKe,eAAef,KAAKgB,gBAAgBG,OAGlD,eAAAV,GACET,KAAKG,0BAA4BH,KAAKG,2BACtCH,KAAKG,8BAA2BiB,EAGlC,cAAAhB,CAAeiB,EAAaC,EAAoBC,GAC9C,OAAOvB,KAAKkB,eAAed,eAAe,CAAEiB,KAAIC,YAAWC,UAG7D,cAAAR,CAAeE,GAEb,OADYjB,KAAKwB,YACNC,WAAWC,QAAQT,GAGhC,iBAAAU,CAAkBJ,GAChBvB,KAAKC,yBAA0B,EAC/BD,KAAKW,WAAaY,GA1Eb1B,EAAA+B,OAAS,CACdL,MAAOM,OACPZ,UAAWa,QAENjC,EAAAkC,QAAU,CAAC,SCRb,MAAMC,EAAoBC,GACxBA,EAAIC,QAAQ,kBAAmB,SAASC,oBCqCpCC,EAKX,WAAArC,CAAYwB,EAAyB,IACnCvB,KAAKqC,WAAa,IAAIC,IACtBtC,KAAKwB,YAAcxB,KAAKuC,uBAAuBhB,EAAMC,aACrDxB,KAAKwB,YAAYC,WAAazB,KAC9BA,KAAKwB,YAAYgB,SAAS,cAAe3C,GAEzC4C,SAASC,iBAAiB,8BAA+BC,IACvD,MAAMC,EAAkBD,GAClBE,OAAEA,EAAMC,OAAEA,GAAWF,EAEvBC,EAAOE,aAAa,oBAAoBC,SAAS,iBACnDH,EAAOI,aACL,+BACAH,EAAOI,WAAWH,aAAa,iCAC7B,MAEJJ,EAAMQ,qBAKZ,QAAAX,CACErB,EACAiC,EACAnC,EACAoC,GAGA,GADAA,IAAAA,EAAexD,GACXG,KAAKqC,WAAWiB,IAAIF,GACtB,MAAM,IAAIG,MAAM,cAAcH,6BAIhC,GAFApD,KAAKqC,WAAWmB,IAAIJ,EAAM,CAAEnC,YAAWE,WAEnCkC,EAAY,CACd,MAAMI,EAAiB,eAAezB,EAAiBoB,GAAMlB,QAAQ,IAAK,QAC1ElC,KAAKwB,YAAYgB,SAASiB,EAAgBJ,IAI9C,OAAA3B,CAAQ0B,GACN,MAAMnC,EAAYjB,KAAKqC,WAAWqB,IAAIN,GACtC,IAAKnC,EACH,MAAM,IAAIsC,MAAM,sBAAsBH,KAExC,OAAOnC,EAGD,sBAAAsB,CAAuBoB,GAC7B,IAAInC,EAAcmC,GAAeC,OAAOC,SAMxC,OAJKrC,IACHA,EAAcsC,EAAYC,QAC1BH,OAAOC,SAAWrC,GAEbA,GAIL,SAAUwC,EAAyB7C,GACvC,MAAO,CACLM,EACA2B,EACAnC,EACAoC,KAEA5B,EAAWe,SAASrB,EAAQiC,EAAMnC,EAAWoC,EAAW,CAE5D,CC3FA,MAAMY,EAAmBb,IACvB,MAAMK,EAAiBzB,EAAiBoB,GAAMlB,QAAQ,IAAK,MAE3D,MAAO,CAAC,gBAAgBuB,IAAkB,eAAeA,IAAiB,EAG/DS,EAAyB,EACpC/C,SACAM,aACAY,aACA8B,kBAEA,MAAMC,EAAoBD,GAAe,GAEzC,IAAK,MAAME,OAAEA,EAAMC,SAAEA,KAAcjC,EAAY,CAC7C,MAAMe,EAAOkB,EACVpC,QAAQ,SAAU,IAClBA,QAAQ,qBAAsB,IAE3BqC,EAAcN,EAAgBb,GAE9BC,EAAae,EAAkBI,MAAK,EAAGC,gBAC3CF,EAAYvB,SAASyB,KAEjBxD,EAAYoD,EAAOK,SAAWL,EAEhChB,EACF5B,EAAWe,SACTrB,EACAiC,EACAnC,EACAoC,EAAWsB,uBAGblD,EAAWe,SAASrB,EAAQiC,EAAMnC"}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
react:
|
2
|
+
pins: "react react-dom react-dom/client"
|
3
|
+
npm_packages: "react react-dom"
|
4
|
+
vite_plugin: "@vitejs/plugin-react"
|
5
|
+
extension: "jsx"
|
6
|
+
|
7
|
+
vue:
|
8
|
+
pins: "vue"
|
9
|
+
npm_packages: "vue"
|
10
|
+
vite_plugin: "@vitejs/plugin-vue"
|
11
|
+
extension: "vue"
|
12
|
+
|
13
|
+
svelte4:
|
14
|
+
pins: "svelte"
|
15
|
+
npm_packages: "svelte@4"
|
16
|
+
vite_plugin: "@sveltejs/vite-plugin-svelte"
|
17
|
+
extension: "svelte"
|
18
|
+
|
19
|
+
svelte:
|
20
|
+
pins: "svelte"
|
21
|
+
npm_packages: "svelte@5"
|
22
|
+
vite_plugin: "@sveltejs/vite-plugin-svelte"
|
23
|
+
extension: "svelte"
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module TurboMount
|
2
|
+
module Generators
|
3
|
+
module Helpers
|
4
|
+
### FS Helpers
|
5
|
+
def js_destination_path
|
6
|
+
return ViteRuby.config.source_code_dir if defined?(ViteRuby)
|
7
|
+
|
8
|
+
if file?("config/vite.json")
|
9
|
+
source_code_dir = JSON.parse(File.read(file_path("config/vite.json"))).dig("all", "sourceCodeDir")
|
10
|
+
return source_code_dir || "app/frontend"
|
11
|
+
end
|
12
|
+
|
13
|
+
"app/javascript"
|
14
|
+
end
|
15
|
+
|
16
|
+
def js_destination_root
|
17
|
+
file_path(js_destination_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def js_entrypoint
|
21
|
+
if vite?
|
22
|
+
js_file_path "entrypoints/application.js"
|
23
|
+
else
|
24
|
+
js_file_path "application.js"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def js_file_path(*relative_path)
|
29
|
+
File.join(js_destination_root, *relative_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def file?(*relative_path)
|
33
|
+
File.file?(file_path(*relative_path))
|
34
|
+
end
|
35
|
+
|
36
|
+
def file_path(*relative_path)
|
37
|
+
File.join(destination_root, *relative_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def vite?
|
41
|
+
file?("config/vite.json") && Dir.glob(file_path("vite.config.*")).any?
|
42
|
+
end
|
43
|
+
|
44
|
+
# Interactivity Helpers
|
45
|
+
def ask(*)
|
46
|
+
unless options[:interactive]
|
47
|
+
say_error "Specify all options when running the generator non-interactively.", :red
|
48
|
+
exit(1)
|
49
|
+
end
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def yes?(*)
|
55
|
+
return false unless options[:interactive]
|
56
|
+
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -13,6 +13,6 @@ const turboMount = new TurboMount();
|
|
13
13
|
// If you want to automatically register components use:
|
14
14
|
// import { registerComponents } from "turbo-mount/registerComponents/<%= framework %>";
|
15
15
|
// const controllers = import.meta.glob("/controllers/**/*_controller.js", { eager: true });
|
16
|
-
// const components = import.meta.glob("/components
|
16
|
+
// const components = import.meta.glob("/components/**/*.<%= extension %>", { eager: true });
|
17
17
|
// registerComponents({ turboMount, components, controllers });
|
18
18
|
<%- end -%>
|
@@ -1,37 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "yaml"
|
4
|
+
require "rails/generators"
|
5
|
+
require "rails/generators/base"
|
6
|
+
|
7
|
+
require_relative "helpers"
|
8
|
+
require_relative "js_package_manager"
|
9
|
+
|
3
10
|
module TurboMount
|
4
11
|
module Generators
|
5
12
|
class InstallGenerator < Rails::Generators::Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
npm_packages: "react react-dom",
|
10
|
-
vite_plugin: "@vitejs/plugin-react"
|
11
|
-
},
|
12
|
-
"vue" => {
|
13
|
-
pins: "vue",
|
14
|
-
npm_packages: "vue",
|
15
|
-
vite_plugin: "@vitejs/plugin-vue"
|
16
|
-
},
|
17
|
-
"svelte" => {
|
18
|
-
pins: "svelte",
|
19
|
-
npm_packages: "svelte",
|
20
|
-
vite_plugin: "@sveltejs/vite-plugin-svelte"
|
21
|
-
}
|
22
|
-
}.freeze
|
13
|
+
include Helpers
|
14
|
+
|
15
|
+
FRAMEWORKS = YAML.load_file(File.expand_path("./frameworks.yml", __dir__))
|
23
16
|
|
24
17
|
source_root File.expand_path("install", __dir__)
|
25
18
|
|
19
|
+
class_option :framework, type: :string,
|
20
|
+
desc: "The framework you want to use with Turbo Mount",
|
21
|
+
enum: FRAMEWORKS.keys,
|
22
|
+
default: nil
|
23
|
+
|
24
|
+
class_option :package_manager, type: :string, default: nil,
|
25
|
+
enum: JSPackageManager.package_managers,
|
26
|
+
desc: "The package manager you want to use to install Turbo Mount"
|
27
|
+
|
28
|
+
class_option :verbose, type: :boolean, default: false,
|
29
|
+
desc: "Run the generator in verbose mode"
|
30
|
+
|
26
31
|
def install
|
27
32
|
say "Installing Turbo Mount"
|
28
33
|
|
29
|
-
|
30
|
-
say "Could not find a package.json or config/importmap.rb file to add the turbo-mount dependency to, please add it manually.", :red
|
31
|
-
exit!
|
32
|
-
end
|
34
|
+
package_manager.validate!
|
33
35
|
|
34
|
-
if importmap?
|
36
|
+
if package_manager.importmap?
|
35
37
|
install_importmap
|
36
38
|
else
|
37
39
|
install_nodejs
|
@@ -43,19 +45,10 @@ module TurboMount
|
|
43
45
|
private
|
44
46
|
|
45
47
|
def install_nodejs
|
46
|
-
|
47
|
-
when "npm"
|
48
|
-
run "npm install turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
49
|
-
when "pnpm"
|
50
|
-
run "pnpm install turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
51
|
-
when "yarn"
|
52
|
-
run "yarn add turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
53
|
-
when "bun"
|
54
|
-
run "bun add turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
55
|
-
end
|
48
|
+
package_manager.add_dependencies("turbo-mount", FRAMEWORKS[framework][:npm_packages])
|
56
49
|
|
57
50
|
say "Creating Turbo Mount initializer"
|
58
|
-
template "turbo-mount.js",
|
51
|
+
template "turbo-mount.js", js_file_path("turbo-mount.js")
|
59
52
|
begin
|
60
53
|
append_to_file js_entrypoint, %(import "./turbo-mount"\n)
|
61
54
|
rescue
|
@@ -66,7 +59,7 @@ module TurboMount
|
|
66
59
|
|
67
60
|
def install_importmap
|
68
61
|
say "Creating Turbo Mount initializer"
|
69
|
-
template "turbo-mount.js",
|
62
|
+
template "turbo-mount.js", js_file_path("turbo-mount-initializer.js")
|
70
63
|
append_to_file "app/javascript/application.js", %(import "turbo-mount-initializer"\n)
|
71
64
|
|
72
65
|
say "Pinning Turbo Mount to the importmap"
|
@@ -75,53 +68,23 @@ module TurboMount
|
|
75
68
|
append_to_file "config/importmap.rb", %(pin "turbo-mount-initializer"\n)
|
76
69
|
|
77
70
|
say "Pinning framework dependencies to the importmap"
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
def js_entrypoint
|
82
|
-
if vite?
|
83
|
-
"app/javascript/entrypoints/application.js"
|
84
|
-
else
|
85
|
-
"app/javascript/application.js"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def vite?
|
90
|
-
Dir.glob(Rails.root.join("vite.config.*")).any?
|
91
|
-
end
|
92
|
-
|
93
|
-
def importmap?
|
94
|
-
build_tool == "importmap"
|
71
|
+
package_manager.add_dependencies(FRAMEWORKS[framework][:pins])
|
95
72
|
end
|
96
73
|
|
97
74
|
def warn_about_vite_plugin
|
98
75
|
say "Make sure to install and add #{FRAMEWORKS[framework][:vite_plugin]} to your Vite config", :yellow
|
99
76
|
end
|
100
77
|
|
101
|
-
def
|
102
|
-
|
103
|
-
|
104
|
-
@build_tool = detect_build_tool
|
78
|
+
def package_manager
|
79
|
+
@package_manager ||= JSPackageManager.new(self)
|
105
80
|
end
|
106
81
|
|
107
|
-
def
|
108
|
-
|
109
|
-
if Rails.root.join("package-lock.json").exist?
|
110
|
-
"npm"
|
111
|
-
elsif Rails.root.join("pnpm-lock.yaml").exist?
|
112
|
-
"pnpm"
|
113
|
-
elsif Rails.root.join("bun.config.js").exist?
|
114
|
-
"bun"
|
115
|
-
else
|
116
|
-
"yarn"
|
117
|
-
end
|
118
|
-
elsif Rails.root.join("config/importmap.rb").exist?
|
119
|
-
"importmap"
|
120
|
-
end
|
82
|
+
def extension
|
83
|
+
FRAMEWORKS[framework][:extension]
|
121
84
|
end
|
122
85
|
|
123
86
|
def framework
|
124
|
-
@framework ||= ask("What framework do you want to use with Turbo Mount?", limited_to: FRAMEWORKS.keys, default: "react")
|
87
|
+
@framework ||= options[:framework] || ask("What framework do you want to use with Turbo Mount?", :green, limited_to: FRAMEWORKS.keys, default: "react")
|
125
88
|
end
|
126
89
|
end
|
127
90
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module TurboMount
|
2
|
+
module Generators
|
3
|
+
class JSPackageManager
|
4
|
+
def self.package_managers
|
5
|
+
%w[npm yarn bun pnpm importmap]
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(generator)
|
9
|
+
@generator = generator
|
10
|
+
@package_manager = generator.options[:package_manager] || detect_package_manager
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate!
|
14
|
+
return if @package_manager.present?
|
15
|
+
|
16
|
+
@generator.say "Could not find a package.json or config/importmap.rb file to add the turbo-mount dependency to, please add it manually.", :red
|
17
|
+
exit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def importmap?
|
21
|
+
@package_manager == "importmap"
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_dependencies(*dependencies)
|
25
|
+
cmd =
|
26
|
+
if importmap?
|
27
|
+
"bin/importmap pin #{dependencies.join(" ")}"
|
28
|
+
else
|
29
|
+
"#{@package_manager} add #{dependencies.join(" ")}#{@generator.options[:verbose] ? "" : " --silent"}"
|
30
|
+
end
|
31
|
+
@generator.in_root { @generator.run cmd }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def detect_package_manager
|
37
|
+
if file?("package.json")
|
38
|
+
if file?("package-lock.json")
|
39
|
+
"npm"
|
40
|
+
elsif file?("pnpm-lock.yaml")
|
41
|
+
"pnpm"
|
42
|
+
elsif file?("bun.lockb")
|
43
|
+
"bun"
|
44
|
+
else
|
45
|
+
"yarn"
|
46
|
+
end
|
47
|
+
elsif file?("config/importmap.rb")
|
48
|
+
"importmap"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def file?(*relative_path)
|
53
|
+
@generator.file?(*relative_path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/turbo/mount/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-mount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Svyatoslav Kryukov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -44,11 +44,17 @@ files:
|
|
44
44
|
- app/assets/javascripts/turbo-mount/svelte.js
|
45
45
|
- app/assets/javascripts/turbo-mount/svelte.min.js
|
46
46
|
- app/assets/javascripts/turbo-mount/svelte.min.js.map
|
47
|
+
- app/assets/javascripts/turbo-mount/svelte4.js
|
48
|
+
- app/assets/javascripts/turbo-mount/svelte4.min.js
|
49
|
+
- app/assets/javascripts/turbo-mount/svelte4.min.js.map
|
47
50
|
- app/assets/javascripts/turbo-mount/vue.js
|
48
51
|
- app/assets/javascripts/turbo-mount/vue.min.js
|
49
52
|
- app/assets/javascripts/turbo-mount/vue.min.js.map
|
53
|
+
- lib/generators/turbo_mount/frameworks.yml
|
54
|
+
- lib/generators/turbo_mount/helpers.rb
|
50
55
|
- lib/generators/turbo_mount/install/turbo-mount.js.tt
|
51
56
|
- lib/generators/turbo_mount/install_generator.rb
|
57
|
+
- lib/generators/turbo_mount/js_package_manager.rb
|
52
58
|
- lib/turbo/mount.rb
|
53
59
|
- lib/turbo/mount/engine.rb
|
54
60
|
- lib/turbo/mount/helpers.rb
|