salvia 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +152 -0
- data/assets/components/Island.tsx +15 -0
- data/assets/islands/Counter.tsx +17 -0
- data/assets/javascripts/islands.js +55 -0
- data/assets/pages/Home.tsx +19 -0
- data/assets/scripts/build.ts +298 -0
- data/assets/scripts/deno.json +15 -0
- data/assets/scripts/deno.lock +56 -0
- data/assets/scripts/sidecar.ts +167 -0
- data/assets/scripts/vendor_setup.ts +25 -0
- data/exe/salvia +8 -0
- data/lib/salvia/cli.rb +189 -0
- data/lib/salvia/compiler/adapters/deno_sidecar.rb +23 -0
- data/lib/salvia/compiler.rb +29 -0
- data/lib/salvia/core/configuration.rb +30 -0
- data/lib/salvia/core/error.rb +7 -0
- data/lib/salvia/core/import_map.rb +36 -0
- data/lib/salvia/core/path_resolver.rb +43 -0
- data/lib/salvia/helpers/island.rb +251 -0
- data/lib/salvia/helpers/tag.rb +47 -0
- data/lib/salvia/helpers.rb +8 -0
- data/lib/salvia/railtie.rb +33 -0
- data/lib/salvia/server/dev_server.rb +83 -0
- data/lib/salvia/server/sidecar.rb +136 -0
- data/lib/salvia/server/sidecar.ts +167 -0
- data/lib/salvia/ssr/dom_mock.rb +46 -0
- data/lib/salvia/ssr/quickjs.rb +282 -0
- data/lib/salvia/ssr.rb +119 -0
- data/lib/salvia/version.rb +5 -0
- data/lib/salvia.rb +68 -0
- metadata +165 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as esbuild from "https://deno.land/x/esbuild@v0.24.2/mod.js";
|
|
2
|
+
import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@0.11";
|
|
3
|
+
|
|
4
|
+
// Use port 0 to let OS assign a free port
|
|
5
|
+
const PORT = 0;
|
|
6
|
+
|
|
7
|
+
console.log(`[Deno Init] 🚀 Salvia Sidecar starting...`);
|
|
8
|
+
|
|
9
|
+
const handler = async (request: Request): Promise<Response> => {
|
|
10
|
+
if (request.method !== "POST") {
|
|
11
|
+
return new Response("Method Not Allowed", { status: 405 });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const body = await request.json();
|
|
16
|
+
const { command, params } = body;
|
|
17
|
+
|
|
18
|
+
if (command === "bundle") {
|
|
19
|
+
const { entryPoint, externals, format, globalName, configPath } = params;
|
|
20
|
+
|
|
21
|
+
// If format is IIFE, we need to handle externals by mapping them to globals
|
|
22
|
+
// But esbuild doesn't support this out of the box for IIFE with externals.
|
|
23
|
+
// We can use a plugin to rewrite imports to globals if they are in externals list.
|
|
24
|
+
|
|
25
|
+
// 4. Global Externals (for IIFE format)
|
|
26
|
+
const globalExternalsPlugin = {
|
|
27
|
+
name: "global-externals",
|
|
28
|
+
setup(build: any) {
|
|
29
|
+
// Preact
|
|
30
|
+
build.onResolve({ filter: /^preact$/ }, (args: any) => {
|
|
31
|
+
return { path: args.path, namespace: "global-external" };
|
|
32
|
+
});
|
|
33
|
+
// Hooks
|
|
34
|
+
build.onResolve({ filter: /^preact\/hooks$/ }, (args: any) => {
|
|
35
|
+
return { path: args.path, namespace: "global-external" };
|
|
36
|
+
});
|
|
37
|
+
// Signals
|
|
38
|
+
build.onResolve({ filter: /^@preact\/signals$/ }, (args: any) => {
|
|
39
|
+
return { path: args.path, namespace: "global-external" };
|
|
40
|
+
});
|
|
41
|
+
// JSX Runtime
|
|
42
|
+
build.onResolve({ filter: /^preact\/jsx-runtime$/ }, (args: any) => {
|
|
43
|
+
return { path: args.path, namespace: "global-external" };
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
build.onLoad({ filter: /.*/, namespace: "global-external" }, (args: any) => {
|
|
47
|
+
if (args.path === "preact") return { contents: "module.exports = globalThis.Preact;", loader: "js" };
|
|
48
|
+
if (args.path === "preact/hooks") return { contents: "module.exports = globalThis.PreactHooks;", loader: "js" };
|
|
49
|
+
if (args.path === "@preact/signals") return { contents: "module.exports = globalThis.PreactSignals;", loader: "js" };
|
|
50
|
+
if (args.path === "preact/jsx-runtime") return { contents: "module.exports = globalThis.PreactJsxRuntime;", loader: "js" };
|
|
51
|
+
return null;
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const externalizePlugin = {
|
|
57
|
+
name: 'externalize-deps',
|
|
58
|
+
setup(build: any) {
|
|
59
|
+
build.onResolve({ filter: /.*/ }, (args: any) => {
|
|
60
|
+
if (externals && externals.includes(args.path)) {
|
|
61
|
+
return { path: args.path, external: true };
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const isIIFE = format === "iife";
|
|
68
|
+
|
|
69
|
+
const plugins = [
|
|
70
|
+
...denoPlugins({ configPath: configPath || `${Deno.cwd()}/salvia/deno.json` })
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
if (isIIFE && !entryPoint.endsWith("vendor_setup.ts")) {
|
|
74
|
+
// IIFEの場合は、globalExternalsPlugin を使う
|
|
75
|
+
// ただし、denoPlugins よりも前に配置して、framework などの解決を横取りする
|
|
76
|
+
plugins.unshift(globalExternalsPlugin);
|
|
77
|
+
} else {
|
|
78
|
+
plugins.unshift(externalizePlugin);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// JIT Bundle for a specific entry point
|
|
82
|
+
const result = await esbuild.build({
|
|
83
|
+
entryPoints: [entryPoint],
|
|
84
|
+
bundle: true,
|
|
85
|
+
format: format || "esm",
|
|
86
|
+
globalName: globalName || undefined, // Exports will be in SalviaComponent.default
|
|
87
|
+
platform: "neutral",
|
|
88
|
+
plugins: plugins,
|
|
89
|
+
external: [], // We handle externals manually with plugins
|
|
90
|
+
write: false, // Return in memory
|
|
91
|
+
// 3. JSX Runtime (Automatic)
|
|
92
|
+
// deno.json の "preact/jsx-runtime" エイリアスを使用
|
|
93
|
+
jsx: "automatic",
|
|
94
|
+
jsxImportSource: "preact",
|
|
95
|
+
minify: false, // Keep it readable for debugging
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const code = result.outputFiles[0].text;
|
|
99
|
+
// Deno.writeTextFileSync("debug_bundle.js", code);
|
|
100
|
+
|
|
101
|
+
return new Response(JSON.stringify({ code }), {
|
|
102
|
+
headers: { "Content-Type": "application/json" },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (command === "check") {
|
|
107
|
+
const { entryPoint, configPath } = params;
|
|
108
|
+
const cmd = new Deno.Command("deno", {
|
|
109
|
+
args: ["check", "--config", configPath || "deno.json", entryPoint],
|
|
110
|
+
stdout: "piped",
|
|
111
|
+
stderr: "piped",
|
|
112
|
+
cwd: Deno.cwd(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const output = await cmd.output();
|
|
116
|
+
const success = output.code === 0;
|
|
117
|
+
const message = new TextDecoder().decode(output.stderr);
|
|
118
|
+
|
|
119
|
+
return new Response(JSON.stringify({ success, message }), {
|
|
120
|
+
headers: { "Content-Type": "application/json" },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (command === "fmt") {
|
|
125
|
+
const { entryPoint, configPath } = params;
|
|
126
|
+
const cmd = new Deno.Command("deno", {
|
|
127
|
+
args: ["fmt", "--config", configPath || "deno.json", entryPoint],
|
|
128
|
+
stdout: "piped",
|
|
129
|
+
stderr: "piped",
|
|
130
|
+
cwd: Deno.cwd(),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const output = await cmd.output();
|
|
134
|
+
const success = output.code === 0;
|
|
135
|
+
const message = new TextDecoder().decode(output.stderr);
|
|
136
|
+
|
|
137
|
+
return new Response(JSON.stringify({ success, message }), {
|
|
138
|
+
headers: { "Content-Type": "application/json" },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (command === "ping") {
|
|
143
|
+
return new Response(JSON.stringify({ status: "pong" }));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return new Response("Unknown command", { status: 400 });
|
|
147
|
+
|
|
148
|
+
} catch (e) {
|
|
149
|
+
const err = e as Error;
|
|
150
|
+
console.error("Sidecar Error:", err);
|
|
151
|
+
return new Response(JSON.stringify({ error: err.message }), { status: 500 });
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const server = Deno.serve({ port: PORT }, handler);
|
|
156
|
+
// Output the assigned port so Ruby can read it
|
|
157
|
+
console.log(`[Deno Init] Listening on http://localhost:${server.addr.port}/`);
|
|
158
|
+
|
|
159
|
+
// Handle cleanup on exit
|
|
160
|
+
const cleanup = () => {
|
|
161
|
+
console.log("🛑 Stopping Sidecar...");
|
|
162
|
+
esbuild.stop();
|
|
163
|
+
Deno.exit();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
Deno.addSignalListener("SIGINT", cleanup);
|
|
167
|
+
Deno.addSignalListener("SIGTERM", cleanup);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { h, Fragment } from "preact";
|
|
2
|
+
import * as preact from "preact";
|
|
3
|
+
import * as hooks from "preact/hooks";
|
|
4
|
+
import * as signals from "@preact/signals";
|
|
5
|
+
import * as jsxRuntime from "preact/jsx-runtime";
|
|
6
|
+
import { renderToString } from "preact-render-to-string";
|
|
7
|
+
|
|
8
|
+
// Expose to global scope for JIT bundles (IIFE)
|
|
9
|
+
(globalThis as any).Preact = preact;
|
|
10
|
+
(globalThis as any).PreactHooks = hooks;
|
|
11
|
+
(globalThis as any).PreactSignals = signals;
|
|
12
|
+
(globalThis as any).PreactJsxRuntime = jsxRuntime;
|
|
13
|
+
(globalThis as any).renderToString = renderToString;
|
|
14
|
+
(globalThis as any).h = h;
|
|
15
|
+
(globalThis as any).Fragment = Fragment;
|
|
16
|
+
|
|
17
|
+
// Simple require shim for JIT bundles
|
|
18
|
+
(globalThis as any).require = function(moduleName: string) {
|
|
19
|
+
if (moduleName === "preact") return preact;
|
|
20
|
+
if (moduleName === "preact/hooks") return hooks;
|
|
21
|
+
if (moduleName === "@preact/signals") return signals;
|
|
22
|
+
if (moduleName === "preact/jsx-runtime") return jsxRuntime;
|
|
23
|
+
if (moduleName === "preact-render-to-string") return { default: renderToString, renderToString };
|
|
24
|
+
throw new Error("Module not found: " + moduleName);
|
|
25
|
+
};
|
data/exe/salvia
ADDED
data/lib/salvia/cli.rb
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Salvia
|
|
7
|
+
class CLI < Thor
|
|
8
|
+
include Thor::Actions
|
|
9
|
+
|
|
10
|
+
def self.source_root
|
|
11
|
+
File.expand_path("../../..", __FILE__)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc "install", "Install Salvia SSR files into your application"
|
|
15
|
+
def install
|
|
16
|
+
say "🌿 Salvia Installer", :bold
|
|
17
|
+
say "===================", :bold
|
|
18
|
+
say ""
|
|
19
|
+
|
|
20
|
+
# 1. Backend Framework
|
|
21
|
+
backend = if File.exist?("bin/rails") || File.exist?("config/application.rb")
|
|
22
|
+
"rails"
|
|
23
|
+
else
|
|
24
|
+
"other"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# 2. Tailwind CSS
|
|
28
|
+
install_tailwind = yes?("1. Do you want to install Tailwind CSS (via tailwindcss-ruby)? (y/N)", :yellow)
|
|
29
|
+
|
|
30
|
+
say ""
|
|
31
|
+
say "🚀 Installing Salvia (Preact + Signals) for #{backend}...", :green
|
|
32
|
+
say ""
|
|
33
|
+
|
|
34
|
+
# Create directories
|
|
35
|
+
empty_directory "salvia/app/islands"
|
|
36
|
+
empty_directory "salvia/app/pages"
|
|
37
|
+
empty_directory "salvia/app/components"
|
|
38
|
+
empty_directory "public/assets/javascripts"
|
|
39
|
+
empty_directory "public/assets/islands"
|
|
40
|
+
empty_directory "salvia"
|
|
41
|
+
|
|
42
|
+
# Copy files
|
|
43
|
+
# Note: source_root is 'salvia/', so paths are relative to that
|
|
44
|
+
copy_file "assets/scripts/deno.json", "salvia/deno.json"
|
|
45
|
+
copy_file "assets/components/Island.tsx", "salvia/app/components/Island.tsx"
|
|
46
|
+
copy_file "assets/islands/Counter.tsx", "salvia/app/islands/Counter.tsx"
|
|
47
|
+
copy_file "assets/pages/Home.tsx", "salvia/app/pages/Home.tsx"
|
|
48
|
+
|
|
49
|
+
create_file "salvia/.gitignore", "/server/\n"
|
|
50
|
+
|
|
51
|
+
# Backend Setup
|
|
52
|
+
case backend
|
|
53
|
+
when "rails"
|
|
54
|
+
create_file "config/initializers/salvia.rb" do
|
|
55
|
+
<<~RUBY
|
|
56
|
+
Salvia.configure do |config|
|
|
57
|
+
config.islands_dir = Rails.root.join("salvia/app/islands")
|
|
58
|
+
config.build_dir = Rails.root.join("public/assets")
|
|
59
|
+
config.ssr_bundle_path = Rails.root.join("salvia/server/ssr_bundle.js")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Initialize SSR Engine
|
|
63
|
+
Salvia::SSR.configure(
|
|
64
|
+
bundle_path: Salvia.config.ssr_bundle_path,
|
|
65
|
+
development: Rails.env.development?
|
|
66
|
+
)
|
|
67
|
+
RUBY
|
|
68
|
+
end
|
|
69
|
+
say " - Created config/initializers/salvia.rb"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Tailwind CSS Setup
|
|
73
|
+
if install_tailwind
|
|
74
|
+
# Add tailwindcss-ruby to Gemfile if present
|
|
75
|
+
if File.exist?("Gemfile")
|
|
76
|
+
unless File.read("Gemfile").include?("tailwindcss-ruby")
|
|
77
|
+
append_to_file "Gemfile", "\ngem 'tailwindcss-ruby'\n"
|
|
78
|
+
say " - Added 'tailwindcss-ruby' to Gemfile"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
empty_directory "app/assets/stylesheets"
|
|
83
|
+
create_file "app/assets/stylesheets/application.tailwind.css" do
|
|
84
|
+
<<~CSS
|
|
85
|
+
@import "tailwindcss";
|
|
86
|
+
|
|
87
|
+
@source "../../views/**/*.erb";
|
|
88
|
+
@source "../../islands/**/*.{js,jsx,tsx}";
|
|
89
|
+
@source "../../../public/assets/javascripts/**/*.js";
|
|
90
|
+
|
|
91
|
+
@theme {
|
|
92
|
+
--color-salvia-500: #6A5ACD;
|
|
93
|
+
--color-salvia-600: #5a4ab8;
|
|
94
|
+
}
|
|
95
|
+
CSS
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
say " - app/assets/stylesheets/ : Tailwind CSS entry point created (v4)"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
say ""
|
|
102
|
+
say "✅ Salvia SSR installed!", :green
|
|
103
|
+
say " - salvia/app/islands/ : Put your interactive Island components here"
|
|
104
|
+
say " - salvia/app/pages/ : Put your static Server Components here (JS-free)"
|
|
105
|
+
say " - salvia/app/components/ : Put your shared/static components here"
|
|
106
|
+
say ""
|
|
107
|
+
say "Next steps:", :yellow
|
|
108
|
+
say " 1. Install Deno: https://deno.land"
|
|
109
|
+
if install_tailwind
|
|
110
|
+
say " 2. Run 'bundle install' to install Tailwind"
|
|
111
|
+
say " 3. Run build: salvia build"
|
|
112
|
+
say " 4. Watch assets: bundle exec foreman start -f Procfile.dev (recommended)"
|
|
113
|
+
else
|
|
114
|
+
say " 2. Run build: salvia build"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
desc "build", "Build Island components for SSR"
|
|
119
|
+
method_option :verbose, aliases: "-v", type: :boolean, default: false, desc: "Verbose output"
|
|
120
|
+
def build
|
|
121
|
+
check_deno_installed!
|
|
122
|
+
|
|
123
|
+
say "🏝️ Building Island components...", :green
|
|
124
|
+
|
|
125
|
+
# Use internal build script
|
|
126
|
+
build_script = File.expand_path("../../../assets/scripts/build.ts", __FILE__)
|
|
127
|
+
|
|
128
|
+
# Use user's deno.json if available, otherwise fallback to internal
|
|
129
|
+
user_config = File.expand_path("salvia/deno.json")
|
|
130
|
+
config_path = if File.exist?(user_config)
|
|
131
|
+
user_config
|
|
132
|
+
else
|
|
133
|
+
File.expand_path("../../../assets/scripts/deno.json", __FILE__)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
cmd = "deno run --allow-all --config #{config_path} #{build_script}"
|
|
137
|
+
cmd += " --verbose" if options[:verbose]
|
|
138
|
+
|
|
139
|
+
success = system(cmd)
|
|
140
|
+
|
|
141
|
+
if success
|
|
142
|
+
say "✅ SSR build completed!", :green
|
|
143
|
+
else
|
|
144
|
+
say "❌ SSR build failed", :red
|
|
145
|
+
exit 1
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
desc "watch", "Watch and rebuild Island components"
|
|
150
|
+
method_option :verbose, aliases: "-v", type: :boolean, default: false, desc: "Verbose output"
|
|
151
|
+
def watch
|
|
152
|
+
check_deno_installed!
|
|
153
|
+
|
|
154
|
+
say "👀 Watching Island components...", :green
|
|
155
|
+
|
|
156
|
+
# Use internal build script
|
|
157
|
+
build_script = File.expand_path("../../../assets/scripts/build.ts", __FILE__)
|
|
158
|
+
|
|
159
|
+
# Use user's deno.json if available, otherwise fallback to internal
|
|
160
|
+
user_config = File.expand_path("salvia/deno.json")
|
|
161
|
+
config_path = if File.exist?(user_config)
|
|
162
|
+
user_config
|
|
163
|
+
else
|
|
164
|
+
File.expand_path("../../../assets/scripts/deno.json", __FILE__)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
cmd = "deno run --allow-all --config #{config_path} #{build_script} --watch"
|
|
168
|
+
cmd += " --verbose" if options[:verbose]
|
|
169
|
+
|
|
170
|
+
exec cmd
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
desc "version", "Display Salvia version"
|
|
174
|
+
def version
|
|
175
|
+
require "salvia/version"
|
|
176
|
+
say "Salvia #{Salvia::VERSION}"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
def check_deno_installed!
|
|
182
|
+
unless system("which deno > /dev/null 2>&1")
|
|
183
|
+
say "❌ Deno is not installed.", :red
|
|
184
|
+
say "Please install Deno: https://deno.land", :yellow
|
|
185
|
+
exit 1
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Salvia
|
|
2
|
+
class Compiler
|
|
3
|
+
module Adapters
|
|
4
|
+
class DenoSidecar
|
|
5
|
+
def bundle(entry_point, **options)
|
|
6
|
+
Salvia::Server::Sidecar.instance.bundle(entry_point, **options)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def check(entry_point, **options)
|
|
10
|
+
Salvia::Server::Sidecar.instance.check(entry_point, **options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fmt(entry_point, **options)
|
|
14
|
+
Salvia::Server::Sidecar.instance.fmt(entry_point, **options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def shutdown
|
|
18
|
+
Salvia::Server::Sidecar.instance.stop
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Salvia
|
|
2
|
+
class Compiler
|
|
3
|
+
class Error < Salvia::Error; end
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
attr_writer :adapter
|
|
7
|
+
|
|
8
|
+
def adapter
|
|
9
|
+
@adapter ||= Salvia::Compiler::Adapters::DenoSidecar.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def bundle(entry_point, **options)
|
|
13
|
+
adapter.bundle(entry_point, **options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check(entry_point)
|
|
17
|
+
adapter.check(entry_point)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def fmt(entry_point)
|
|
21
|
+
adapter.fmt(entry_point)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def shutdown
|
|
25
|
+
adapter.shutdown if adapter.respond_to?(:shutdown)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Salvia
|
|
4
|
+
module Core
|
|
5
|
+
class Configuration
|
|
6
|
+
attr_accessor :ssr_bundle_path, :island_inspector, :islands_dir, :build_dir, :deno_config_path, :root
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@root = Dir.pwd
|
|
10
|
+
@ssr_bundle_path = "salvia/server/ssr_bundle.js"
|
|
11
|
+
@islands_dir = "salvia/app/islands"
|
|
12
|
+
@build_dir = "public/assets"
|
|
13
|
+
|
|
14
|
+
user_deno_json = File.join(@root, "salvia/deno.json")
|
|
15
|
+
if File.exist?(user_deno_json)
|
|
16
|
+
@deno_config_path = user_deno_json
|
|
17
|
+
else
|
|
18
|
+
@deno_config_path = File.expand_path("../../../assets/scripts/deno.json", __dir__)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@island_inspector = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def island_inspector?
|
|
25
|
+
return @island_inspector unless @island_inspector.nil?
|
|
26
|
+
Salvia.development?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Salvia
|
|
6
|
+
module Core
|
|
7
|
+
class ImportMap
|
|
8
|
+
def self.load
|
|
9
|
+
new.load
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def load
|
|
13
|
+
path = find_deno_json
|
|
14
|
+
return {} unless path
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
content = File.read(path)
|
|
18
|
+
json = JSON.parse(content)
|
|
19
|
+
json["imports"] || {}
|
|
20
|
+
rescue JSON::ParserError
|
|
21
|
+
{}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def keys
|
|
26
|
+
load.keys
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def find_deno_json
|
|
32
|
+
Salvia.config.deno_config_path
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Salvia
|
|
4
|
+
module Core
|
|
5
|
+
class PathResolver
|
|
6
|
+
def self.resolve(name)
|
|
7
|
+
# Check flat structure
|
|
8
|
+
path = File.join(Salvia.root, "#{name}.tsx")
|
|
9
|
+
return path if File.exist?(path)
|
|
10
|
+
|
|
11
|
+
roots = [
|
|
12
|
+
"salvia/app/pages",
|
|
13
|
+
"salvia/app/islands",
|
|
14
|
+
"salvia/app/components"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
roots.each do |root|
|
|
18
|
+
path = File.join(Salvia.root, root, "#{name}.tsx")
|
|
19
|
+
return path if File.exist?(path)
|
|
20
|
+
|
|
21
|
+
path = File.join(Salvia.root, root, "#{name}.jsx")
|
|
22
|
+
return path if File.exist?(path)
|
|
23
|
+
|
|
24
|
+
path = File.join(Salvia.root, root, "#{name}.js")
|
|
25
|
+
return path if File.exist?(path)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if name.include?("/")
|
|
29
|
+
path = File.join(Salvia.root, "salvia/app", "#{name}.tsx")
|
|
30
|
+
return path if File.exist?(path)
|
|
31
|
+
|
|
32
|
+
path = File.join(Salvia.root, "salvia/app", "#{name}.jsx")
|
|
33
|
+
return path if File.exist?(path)
|
|
34
|
+
|
|
35
|
+
path = File.join(Salvia.root, "salvia/app", "#{name}.js")
|
|
36
|
+
return path if File.exist?(path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|