diff --git a/.github/workflows/generators.yml b/.github/workflows/generators.yml
index ba560106..fc98834d 100644
--- a/.github/workflows/generators.yml
+++ b/.github/workflows/generators.yml
@@ -18,14 +18,23 @@ jobs:
test:
runs-on: ubuntu-latest
strategy:
+ fail-fast: false
matrix:
- framework: [react, vue, svelte4, svelte]
+ framework: [react]
typescript: [true, false]
tailwind: [true, false]
ruby: ['3.3']
node: ['22']
+ inertia_version: ['1.2.0', '1.3.0-beta.2', '2.0.0-beta.2']
+ exclude:
+ # 1.2.0 does not support typescript
+ - typescript: true
+ inertia_version: '1.2.0'
+ # 1.2.0 doesn't support Svelte 5
+ - framework: svelte
+ inertia_version: '1.2.0'
- name: ${{ matrix.framework }} (TS:${{ matrix.typescript }}, TW:${{ matrix.tailwind }})
+ name: ${{ matrix.framework }} (TS:${{ matrix.typescript }}, TW:${{ matrix.tailwind }}, Inertia:${{ matrix.inertia_version }})
steps:
- uses: actions/checkout@v4
@@ -48,7 +57,7 @@ jobs:
tmp/bundle_cache
tmp/npm_cache
~/.npm
- key: ${{ runner.os }}-deps-${{ matrix.framework }}-${{ hashFiles('**/Gemfile.lock') }}-${{ github.sha }}
+ key: ${{ runner.os }}-deps-${{ matrix.framework }}-${{ matrix.inertia_version }}-${{ hashFiles('**/Gemfile.lock') }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-deps-${{ matrix.framework }}-
${{ runner.os }}-deps-
@@ -60,13 +69,13 @@ jobs:
run: |
ts_flag=${{ matrix.typescript && '--typescript' || '--no-typescript' }}
tw_flag=${{ matrix.tailwind && '--tailwind' || '--no-tailwind' }}
- bin/generate_scaffold_example --framework=${{ matrix.framework }} $ts_flag $tw_flag
+ bin/generate_scaffold_example --framework=${{ matrix.framework }} --inertia-version=${{ matrix.inertia_version }} $ts_flag $tw_flag
- name: Upload test artifacts
if: failure()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: test-output-${{ matrix.framework }}-ts${{ matrix.typescript }}-tw${{ matrix.tailwind }}
+ name: test-output-${{ matrix.framework }}-ts${{ matrix.typescript }}-tw${{ matrix.tailwind }}-v${{ matrix.inertia_version }}
path: |
tmp/scaffold_example/log
tmp/scaffold_example/tmp/screenshots
diff --git a/bin/generate_scaffold_example b/bin/generate_scaffold_example
index 44cf3fe6..e127efb9 100755
--- a/bin/generate_scaffold_example
+++ b/bin/generate_scaffold_example
@@ -26,13 +26,17 @@ OptionParser.new do |opts|
opts.on('--[no-]tailwind', 'Enable/disable Tailwind') do |t|
options[:tailwind] = t
end
+
+ opts.on('--inertia-version VERSION', 'Specify Inertia version') do |v|
+ options[:inertia_version] = v
+ end
end.parse!
# Build generator args string
generator_args = "--framework=#{options[:framework]}"
generator_args += ' --typescript' if options[:typescript]
-generator_args += ' --install-vite'
-generator_args += ' --install-tailwind' if options[:tailwind]
+generator_args += ' --tailwind' if options[:tailwind]
+generator_args += " --inertia-version=#{options[:inertia_version]}" if options[:inertia_version]
# Setup paths relative to project root
project_root = File.expand_path('..', __dir__)
@@ -57,25 +61,28 @@ system("rails new #{app_dir} -J")
# Install and configure with caching
Dir.chdir(app_dir) do
# Configure bundler to use cache in project root
- system("bundle config set --local path '#{gem_cache}'")
+ system("bundle config set --local path '#{gem_cache}'", exception: true)
# Configure npm to use cache in project root
- system("npm config set cache '#{npm_cache}'")
+ system("npm config set cache '#{npm_cache}'", exception: true)
# Install dependencies
- system('bundle add inertia_rails --path ../../')
- system('bundle add bcrypt')
- system('bin/rails active_storage:install')
+ system('bundle add inertia_rails --path ../../', exception: true)
+ system('bundle add bcrypt', exception: true)
+ system('bin/rails active_storage:install', exception: true)
# Run install generator with configured options
- system("bin/rails g inertia:install --no-interactive --force #{generator_args}")
+ system("bin/rails g inertia:install --no-interactive --force --vite #{generator_args} --verbose", exception: true)
# Generate a scaffold
- system('bin/rails g inertia:scaffold user name email admin:boolean password:digest avatar:attachment')
- system('bin/rails g inertia:scaffold post content:text published_at:date gallery:attachments')
- system('bin/rails db:migrate')
+ system('bin/rails g inertia:scaffold user name email admin:boolean password:digest avatar:attachment', exception: true)
+ system('bin/rails g inertia:scaffold post content:text published_at:date gallery:attachments', exception: true)
+ system('bin/rails db:migrate', exception: true)
# Run tests
- system('bin/rails test')
- system('bin/rails test:system')
+ system('bin/rails test', exception: true)
+ system('bin/rails test:system', exception: true)
+
+ # Lint code
+ system('npm run check', exception: true) if options[:typescript]
end
diff --git a/docs/cookbook/integrating-shadcn-ui.md b/docs/cookbook/integrating-shadcn-ui.md
index c8729b5e..82303646 100644
--- a/docs/cookbook/integrating-shadcn-ui.md
+++ b/docs/cookbook/integrating-shadcn-ui.md
@@ -14,7 +14,7 @@ If you're starting fresh, create a new Rails application with Inertia (or skip t
rails new -JA shadcn-inertia-rails
cd shadcn-inertia-rails
-rails generate inertia:install `--framework=react --typescript --install-vite --install-tailwind --no-interactive`
+rails generate inertia:install `--framework=react --typescript --vite --tailwind --no-interactive`
Installing Inertia's Rails adapter
...
```
@@ -25,7 +25,7 @@ Installing Inertia's Rails adapter
rails new -JA shadcn-inertia-rails
cd shadcn-inertia-rails
-rails generate inertia:install --framework=react --install-vite --install-tailwind --no-interactive
+rails generate inertia:install --framework=react --vite --tailwind --no-interactive
Installing Inertia's Rails adapter
...
```
diff --git a/lib/generators/inertia/install/install_generator.rb b/lib/generators/inertia/install/install_generator.rb
index 119001a0..6eca99eb 100644
--- a/lib/generators/inertia/install/install_generator.rb
+++ b/lib/generators/inertia/install/install_generator.rb
@@ -34,10 +34,10 @@ class InstallGenerator < Rails::Generators::Base
class_option :interactive, type: :boolean, default: true,
desc: 'Whether to prompt for optional installations'
- class_option :install_tailwind, type: :boolean, default: false,
- desc: 'Whether to install Tailwind CSS'
- class_option :install_vite, type: :boolean, default: false,
- desc: 'Whether to install Vite Ruby'
+ class_option :tailwind, type: :boolean, default: false,
+ desc: 'Whether to install Tailwind CSS'
+ class_option :vite, type: :boolean, default: false,
+ desc: 'Whether to install Vite Ruby'
class_option :example_page, type: :boolean, default: true,
desc: 'Whether to add an example Inertia page'
@@ -127,6 +127,11 @@ def install_typescript
end
add_dependencies(*FRAMEWORKS[framework]['packages_ts'])
+
+ say 'Copying adding scripts to package.json'
+ run 'npm pkg set scripts.check="svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json"' if svelte?
+ run 'npm pkg set scripts.check="vue-tsc -p tsconfig.app.json && tsc -p tsconfig.node.json"' if framework == 'vue'
+ run 'npm pkg set scripts.check="tsc -p tsconfig.app.json && tsc -p tsconfig.node.json"' if framework == 'react'
end
def install_example_page
@@ -236,13 +241,13 @@ def vite_config_path
def install_vite?
return @install_vite if defined?(@install_vite)
- @install_vite = options[:install_vite] || yes?('Would you like to install Vite Ruby? (y/n)', :green)
+ @install_vite = options[:vite] || yes?('Would you like to install Vite Ruby? (y/n)', :green)
end
def install_tailwind?
return @install_tailwind if defined?(@install_tailwind)
- @install_tailwind = options[:install_tailwind] || yes?('Would you like to install Tailwind CSS? (y/n)', :green)
+ @install_tailwind = options[:tailwind] || yes?('Would you like to install Tailwind CSS? (y/n)', :green)
end
def typescript?
diff --git a/lib/generators/inertia/install/templates/react/inertia.js b/lib/generators/inertia/install/templates/react/inertia.js
index f00cad69..87d08702 100644
--- a/lib/generators/inertia/install/templates/react/inertia.js
+++ b/lib/generators/inertia/install/templates/react/inertia.js
@@ -15,20 +15,29 @@ createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.jsx', { eager: true })
- return pages[`../pages/${name}.jsx`]
+ const page = pages[`../pages/${name}.jsx`]
+ if (!page) {
+ console.error(`Missing Inertia page component: '${name}.jsx'`)
+ }
// To use a default layout, import the Layout component
// and use the following lines.
// see https://inertia-rails.netlify.app/guide/pages#default-layouts
//
- // const page = pages[`../pages/${name}.jsx`]
// page.default.layout ||= (page) => createElement(Layout, null, page)
- // return page
+
+ return page
},
setup({ el, App, props }) {
- const root = createRoot(el)
-
- root.render(createElement(App, props))
+ if (el) {
+ createRoot(el).render(createElement(App, props))
+ } else {
+ console.error(
+ 'Missing root element.\n\n' +
+ 'If you see this error, it probably means you load Inertia.js on non-Inertia pages.\n' +
+ 'Consider moving <%%= vite_javascript_tag "inertia" %> to the Inertia-specific layout instead.'
+ )
+ }
},
})
diff --git a/lib/generators/inertia/install/templates/react/inertia.ts b/lib/generators/inertia/install/templates/react/inertia.ts
index 4ef3a280..16f153fa 100644
--- a/lib/generators/inertia/install/templates/react/inertia.ts
+++ b/lib/generators/inertia/install/templates/react/inertia.ts
@@ -16,21 +16,30 @@ createInertiaApp({
// progress: false,
resolve: (name) => {
- const pages = import.meta.glob('../pages/**/*.tsx', { eager: true })
- return pages[`../pages/${name}.tsx`]
+ const pages = import.meta.glob('../pages/**/*.tsx', {eager: true})
+ const page = pages[`../pages/${name}.tsx`]
+ if (!page) {
+ console.error(`Missing Inertia page component: '${name}.tsx'`)
+ }
// To use a default layout, import the Layout component
- // and use the following lines.
+ // and use the following line.
// see https://inertia-rails.netlify.app/guide/pages#default-layouts
//
- // const page = pages[`../pages/${name}.tsx`]
// page.default.layout ||= (page) => createElement(Layout, null, page)
- // return page
- },
- setup({ el, App, props }) {
- const root = createRoot(el)
+ return page
+ },
- root.render(createElement(App, props))
+ setup({el, App, props}) {
+ if (el) {
+ createRoot(el).render(createElement(App, props))
+ } else {
+ console.error(
+ 'Missing root element.\n\n' +
+ 'If you see this error, it probably means you load Inertia.js on non-Inertia pages.\n' +
+ 'Consider moving <%%= vite_typescript_tag "inertia" %> to the Inertia-specific layout instead.'
+ )
+ }
},
})
diff --git a/lib/generators/inertia/install/templates/svelte/inertia.js b/lib/generators/inertia/install/templates/svelte/inertia.js
index 1ea6c22f..e289e297 100644
--- a/lib/generators/inertia/install/templates/svelte/inertia.js
+++ b/lib/generators/inertia/install/templates/svelte/inertia.js
@@ -14,17 +14,29 @@ createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.svelte', { eager: true })
- return pages[`../pages/${name}.svelte`]
+ const page = pages[`../pages/${name}.svelte`]
+ if (!page) {
+ console.error(`Missing Inertia page component: '${name}.svelte'`)
+ }
// To use a default layout, import the Layout component
- // and use the following lines.
+ // and use the following line.
// see https://inertia-rails.netlify.app/guide/pages#default-layouts
//
- // const page = pages[`../pages/${name}.svelte`]
// return { default: page.default, layout: page.layout || Layout }
+
+ return page
},
setup({ el, App, props }) {
- mount(App, { target: el, props })
+ if (el) {
+ mount(App, { target: el, props })
+ } else {
+ console.error(
+ 'Missing root element.\n\n' +
+ 'If you see this error, it probably means you load Inertia.js on non-Inertia pages.\n' +
+ 'Consider moving <%%= vite_javascript_tag "inertia" %> to the Inertia-specific layout instead.'
+ )
+ }
},
})
diff --git a/lib/generators/inertia/install/templates/svelte/inertia.ts b/lib/generators/inertia/install/templates/svelte/inertia.ts
index d6d55cd6..f524dbda 100644
--- a/lib/generators/inertia/install/templates/svelte/inertia.ts
+++ b/lib/generators/inertia/install/templates/svelte/inertia.ts
@@ -14,17 +14,30 @@ createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.svelte', { eager: true })
- return pages[`../pages/${name}.svelte`]
+ const page = pages[`../pages/${name}.svelte`]
+ if (!page) {
+ console.error(`Missing Inertia page component: '${name}.svelte'`)
+ }
// To use a default layout, import the Layout component
- // and use the following lines.
+ // and use the following line.
// see https://inertia-rails.netlify.app/guide/pages#default-layouts
//
- // const page = pages[`../pages/${name}.svelte`]
// return { default: page.default, layout: page.layout || Layout }
+
+ return page
},
setup({ el, App, props }) {
- mount(App, { target: el, props })
+ if (el) {
+ <%= '// @ts-expect-error 1.3.0 beta contains types mismatch' if inertia_resolved_version == Gem::Version.new('1.3.0-beta.2') -%>
+ mount(App, { target: el, props })
+ } else {
+ console.error(
+ 'Missing root element.\n\n' +
+ 'If you see this error, it probably means you load Inertia.js on non-Inertia pages.\n' +
+ 'Consider moving <%%= vite_typescript_tag "inertia" %> to the Inertia-specific layout instead.'
+ )
+ }
},
})
diff --git a/lib/generators/inertia/install/templates/svelte4/inertia.js b/lib/generators/inertia/install/templates/svelte4/inertia.js
index 7c288a6e..7afb70ab 100644
--- a/lib/generators/inertia/install/templates/svelte4/inertia.js
+++ b/lib/generators/inertia/install/templates/svelte4/inertia.js
@@ -13,17 +13,29 @@ createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.svelte', { eager: true })
- return pages[`../pages/${name}.svelte`]
+ const page = pages[`../pages/${name}.svelte`]
+ if (!page) {
+ console.error(`Missing Inertia page component: '${name}.svelte'`)
+ }
// To use a default layout, import the Layout component
// and use the following lines.
// see https://inertia-rails.netlify.app/guide/pages#default-layouts
//
- // const page = pages[`../pages/${name}.svelte`]
// return { default: page.default, layout: page.layout || Layout }
+
+ return page
},
setup({ el, App, props }) {
- new App({ target: el, props })
+ if (el) {
+ new App({ target: el, props })
+ } else {
+ console.error(
+ 'Missing root element.\n\n' +
+ 'If you see this error, it probably means you load Inertia.js on non-Inertia pages.\n' +
+ 'Consider moving <%%= vite_javascript_tag "inertia" %> to the Inertia-specific layout instead.'
+ )
+ }
},
})
diff --git a/lib/generators/inertia/install/templates/svelte4/inertia.ts b/lib/generators/inertia/install/templates/svelte4/inertia.ts
index a4a75495..ec543b2b 100644
--- a/lib/generators/inertia/install/templates/svelte4/inertia.ts
+++ b/lib/generators/inertia/install/templates/svelte4/inertia.ts
@@ -13,17 +13,29 @@ createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.svelte', { eager: true })
- return pages[`../pages/${name}.svelte`]
+ const page = pages[`../pages/${name}.svelte`]
+ if (!page) {
+ console.error(`Missing Inertia page component: '${name}.svelte'`)
+ }
// To use a default layout, import the Layout component
- // and use the following lines.
+ // and use the following line.
// see https://inertia-rails.netlify.app/guide/pages#default-layouts
//
- // const page = pages[`../pages/${name}.svelte`]
// return { default: page.default, layout: page.layout || Layout }
+
+ return page
},
setup({ el, App, props }) {
- new App({ target: el, props })
+ if (el) {
+ new App({ target: el, props })
+ } else {
+ console.error(
+ 'Missing root element.\n\n' +
+ 'If you see this error, it probably means you load Inertia.js on non-Inertia pages.\n' +
+ 'Consider moving <%%= vite_javascript_tag "inertia" %> to the Inertia-specific layout instead.'
+ )
+ }
},
})
diff --git a/lib/generators/inertia_templates/scaffold/templates/react/Form.tsx.tt b/lib/generators/inertia_templates/scaffold/templates/react/Form.tsx.tt
index 6fdb23f9..3ec75444 100644
--- a/lib/generators/inertia_templates/scaffold/templates/react/Form.tsx.tt
+++ b/lib/generators/inertia_templates/scaffold/templates/react/Form.tsx.tt
@@ -1,7 +1,10 @@
import { FormEvent } from 'react'
-import { useForm, InertiaFormProps } from '@inertiajs/react'
+import { useForm <%= ', InertiaFormProps' if inertia_resolved_version.release >= Gem::Version.new('2.0.0') %>} from '@inertiajs/react'
import { <%= inertia_model_type %>, <%= inertia_model_form_type %> } from './types'
-
+<% if inertia_resolved_version.release < Gem::Version.new('2.0.0') %>
+// Temporary fix for InertiaFormProps not being exported from @inertiajs/react
+type InertiaFormProps> = ReturnType>
+<% end %>
interface FormProps {
<%= singular_table_name %>: <%= inertia_model_type %>
onSubmit: (form: InertiaFormProps<<%= inertia_model_form_type %>>) => void
diff --git a/lib/generators/inertia_templates/scaffold/templates/react/One.jsx.tt b/lib/generators/inertia_templates/scaffold/templates/react/One.jsx.tt
index 5497c21f..b085e03d 100644
--- a/lib/generators/inertia_templates/scaffold/templates/react/One.jsx.tt
+++ b/lib/generators/inertia_templates/scaffold/templates/react/One.jsx.tt
@@ -17,7 +17,7 @@ export default function <%= inertia_component_name %>({ <%= singular_table_name
))}
<% else -%>
- {<%= singular_table_name %>.<%= attribute.column_name %>.toString()}
+ {<%= singular_table_name %>.<%= attribute.column_name %>?.toString()}
<% end -%>
<% end -%>
diff --git a/lib/generators/inertia_templates/scaffold/templates/react/One.tsx.tt b/lib/generators/inertia_templates/scaffold/templates/react/One.tsx.tt
index 9b0f1ace..fa62a5fe 100644
--- a/lib/generators/inertia_templates/scaffold/templates/react/One.tsx.tt
+++ b/lib/generators/inertia_templates/scaffold/templates/react/One.tsx.tt
@@ -23,7 +23,7 @@ export default function <%= inertia_component_name %>({ <%= singular_table_name
))}
<% else -%>
- {<%= singular_table_name %>.<%= attribute.column_name %>.toString()}
+ {<%= singular_table_name %>.<%= attribute.column_name %>?.toString()}
<% end -%>
<% end -%>
diff --git a/lib/generators/inertia_templates/scaffold/templates/react/Show.tsx.tt b/lib/generators/inertia_templates/scaffold/templates/react/Show.tsx.tt
index cdb7ed05..485afe19 100644
--- a/lib/generators/inertia_templates/scaffold/templates/react/Show.tsx.tt
+++ b/lib/generators/inertia_templates/scaffold/templates/react/Show.tsx.tt
@@ -1,4 +1,3 @@
-import { MouseEvent } from 'react'
import { Link, Head } from '@inertiajs/react'
import { <%= inertia_model_type %> } from './types'
import <%= inertia_component_name %> from './<%= inertia_component_name %>'
diff --git a/lib/generators/inertia_tw_templates/scaffold/templates/react/Form.tsx.tt b/lib/generators/inertia_tw_templates/scaffold/templates/react/Form.tsx.tt
index d23e00ea..063f3cc3 100644
--- a/lib/generators/inertia_tw_templates/scaffold/templates/react/Form.tsx.tt
+++ b/lib/generators/inertia_tw_templates/scaffold/templates/react/Form.tsx.tt
@@ -1,7 +1,10 @@
import { FormEvent } from 'react'
-import { useForm, InertiaFormProps } from '@inertiajs/react'
+import { useForm <%= ', InertiaFormProps' if inertia_resolved_version.release >= Gem::Version.new('2.0.0') %>} from '@inertiajs/react'
import { <%= inertia_model_type %>, <%= inertia_model_form_type %> } from './types'
-
+<% if inertia_resolved_version.release < Gem::Version.new('2.0.0') %>
+// Temporary fix for InertiaFormProps not being exported from @inertiajs/react
+type InertiaFormProps> = ReturnType>
+<% end %>
interface FormProps {
<%= singular_table_name %>: <%= inertia_model_type %>
onSubmit: (form: InertiaFormProps<<%= inertia_model_form_type %>>) => void
diff --git a/lib/generators/inertia_tw_templates/scaffold/templates/react/Show.tsx.tt b/lib/generators/inertia_tw_templates/scaffold/templates/react/Show.tsx.tt
index 30f4af52..66acd33a 100644
--- a/lib/generators/inertia_tw_templates/scaffold/templates/react/Show.tsx.tt
+++ b/lib/generators/inertia_tw_templates/scaffold/templates/react/Show.tsx.tt
@@ -1,4 +1,3 @@
-import { MouseEvent } from 'react'
import { Link, Head } from '@inertiajs/react'
import { <%= inertia_model_type %> } from './types'
import <%= inertia_component_name %> from './<%= inertia_component_name %>'
diff --git a/lib/inertia_rails/generators/helper.rb b/lib/inertia_rails/generators/helper.rb
index 1d2d86e8..13f2e5f4 100644
--- a/lib/inertia_rails/generators/helper.rb
+++ b/lib/inertia_rails/generators/helper.rb
@@ -15,6 +15,7 @@ def guess_the_default_framework
'vue'
else
Thor::Shell::Basic.new.say_error 'Could not determine the Inertia.js framework you are using.'
+ exit 1
end
end
@@ -83,6 +84,12 @@ def js_resources_path
route_url
end
+ def inertia_resolved_version
+ @inertia_resolved_version ||= Gem::Version.new(
+ `npm show @inertiajs/core@#{options[:inertia_version]} version`.strip
+ )
+ end
+
def ts_type(attribute)
case attribute.type
when :float, :decimal, :integer
diff --git a/spec/generators/install/install_generator_spec.rb b/spec/generators/install/install_generator_spec.rb
index 35972db4..e7341194 100644
--- a/spec/generators/install/install_generator_spec.rb
+++ b/spec/generators/install/install_generator_spec.rb
@@ -44,8 +44,8 @@
expect { generator }.to raise_error(SystemExit)
end
- context 'with --install-vite' do
- let(:args) { super() + %w[--install-vite] }
+ context 'with --vite' do
+ let(:args) { super() + %w[--vite] }
it 'installs Vite' do
expect { generator }.not_to raise_error
@@ -62,8 +62,8 @@
end
end
- context 'with --install-tailwind' do
- let(:args) { super() + %w[--install-tailwind] }
+ context 'with --tailwind' do
+ let(:args) { super() + %w[--tailwind] }
before { prepare_application }