ruflet 0.0.20 → 0.0.22

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.
@@ -13,23 +13,25 @@ module Ruflet
13
13
  RUNTIME_REPO_URL = ENV.fetch("RUFLET_RUNTIME_REPO_URL", "https://github.com/AdamMusa/ruflet.git")
14
14
 
15
15
  CLIENT_EXTENSION_MAP = {
16
- "audio" => { package: "flet_audio", alias: "ruflet_audio" },
17
- "audio_recorder" => { package: "flet_audio_recorder", alias: "ruflet_audio_recorder" },
18
- "camera" => { package: "flet_camera", alias: "ruflet_camera" },
19
- "charts" => { package: "flet_charts", alias: "ruflet_charts" },
20
- "code_editor" => { package: "flet_code_editor", alias: "ruflet_code_editor" },
21
- "color_pickers" => { package: "flet_color_pickers", alias: "ruflet_color_picker" },
22
- "datatable2" => { package: "flet_datatable2", alias: "ruflet_datatable2" },
23
- "flashlight" => { package: "flet_flashlight", alias: "ruflet_flashlight" },
24
- "geolocator" => { package: "flet_geolocator", alias: "ruflet_geolocator" },
25
- "lottie" => { package: "flet_lottie", alias: "ruflet_lottie" },
26
- "map" => { package: "flet_map", alias: "ruflet_map" },
27
- "permission_handler" => { package: "flet_permission_handler", alias: "ruflet_permission_handler" },
16
+ "ads" => { package: "ruflet_ads", alias: "ruflet_ads" },
17
+ "audio" => { package: "ruflet_audio", alias: "ruflet_audio" },
18
+ "audio_recorder" => { package: "ruflet_audio_recorder", alias: "ruflet_audio_recorder" },
19
+ "camera" => { package: "ruflet_camera", alias: "ruflet_camera" },
20
+ "charts" => { package: "ruflet_charts", alias: "ruflet_charts" },
21
+ "code_editor" => { package: "ruflet_code_editor", alias: "ruflet_code_editor" },
22
+ "color_pickers" => { package: "ruflet_color_pickers", alias: "ruflet_color_picker" },
23
+ "datatable2" => { package: "ruflet_datatable2", alias: "ruflet_datatable2" },
24
+ "flashlight" => { package: "ruflet_flashlight", alias: "ruflet_flashlight" },
25
+ "geolocator" => { package: "ruflet_geolocator", alias: "ruflet_geolocator" },
26
+ "lottie" => { package: "ruflet_lottie", alias: "ruflet_lottie" },
27
+ "map" => { package: "ruflet_map", alias: "ruflet_map" },
28
+ "permission_handler" => { package: "ruflet_permission_handler", alias: "ruflet_permission_handler" },
28
29
  "qrcode_scanner" => { package: "ruflet_qrcode_scanner", alias: "ruflet_qrcode_scanner" },
29
- "rive" => { package: "flet_rive", alias: "ruflet_rive" },
30
- "secure_storage" => { package: "flet_secure_storage", alias: "ruflet_secure_storage" },
31
- "video" => { package: "flet_video", alias: "ruflet_video" },
32
- "webview" => { package: "flet_webview", alias: "ruflet_webview" }
30
+ "rive" => { package: "ruflet_rive", alias: "ruflet_rive" },
31
+ "secure_storage" => { package: "ruflet_secure_storage", alias: "ruflet_secure_storage" },
32
+ "spinkit" => { package: "ruflet_spinkit", alias: "ruflet_spinkit" },
33
+ "video" => { package: "ruflet_video", alias: "ruflet_video" },
34
+ "webview" => { package: "ruflet_webview", alias: "ruflet_webview" }
33
35
  }.freeze
34
36
 
35
37
  def command_new(args)
@@ -51,6 +53,7 @@ module Ruflet
51
53
  File.write(File.join(root, "README.md"), format(Ruflet::CLI::README_TEMPLATE, app_name: File.basename(root)))
52
54
  write_default_ruflet_config(root, File.basename(root))
53
55
  copy_default_project_assets(root)
56
+ copy_default_apple_extensions(root)
54
57
  project_name = File.basename(root)
55
58
  puts "Run:"
56
59
  puts " cd #{project_name}"
@@ -72,7 +75,16 @@ module Ruflet
72
75
  target = hidden_flutter_client_dir(root)
73
76
  FileUtils.mkdir_p(File.dirname(target))
74
77
  FileUtils.rm_rf(target)
75
- FileUtils.cp_r(template_root, target)
78
+ # `target` is the Flutter project root, not a directory that contains
79
+ # the template root. Copying a source directory onto a target that was
80
+ # already created by FileUtils can produce
81
+ # `build/client/ruflet_flutter_template/...`, leaving `build/client`
82
+ # without pubspec.yaml and making Flutter reject it as a project.
83
+ FileUtils.mkdir_p(target)
84
+ Dir.children(template_root).each do |entry|
85
+ copy_client_template_entry(
86
+ template_root, target, entry)
87
+ end
76
88
  prune_client_template(target)
77
89
  write_client_template_revision(target, installed_template_revision(template_root))
78
90
  end
@@ -205,10 +217,16 @@ module Ruflet
205
217
  git_revision(File.expand_path("../..", template_root))
206
218
  end
207
219
 
220
+ # Always record where the client came from. A template resolved from a
221
+ # local checkout may have no readable revision, and skipping the marker
222
+ # entirely left the copied client indistinguishable from one that was
223
+ # never stamped. "local" never matches an expected revision, so the client
224
+ # is refreshed rather than treated as current.
208
225
  def write_client_template_revision(target, revision)
209
- return unless revision
226
+ value = revision.to_s.strip
227
+ value = "local" if value.empty?
210
228
 
211
- File.write(File.join(target, ".ruflet-template-revision"), "#{revision}\n")
229
+ File.write(File.join(target, ".ruflet-template-revision"), "#{value}\n")
212
230
  end
213
231
 
214
232
  def client_template_current?(target, template_root)
@@ -255,6 +273,43 @@ module Ruflet
255
273
  end
256
274
  end
257
275
 
276
+ def copy_client_template_entry(template_root, target, relative)
277
+ return if generated_client_template_path?(relative)
278
+
279
+ source = File.join(template_root, relative)
280
+ destination = File.join(target, relative)
281
+ stat = File.lstat(source)
282
+ if stat.symlink?
283
+ FileUtils.mkdir_p(File.dirname(destination))
284
+ FileUtils.ln_s(File.readlink(source), destination)
285
+ elsif stat.directory?
286
+ FileUtils.mkdir_p(destination)
287
+ Dir.children(source).each do |entry|
288
+ copy_client_template_entry(
289
+ template_root, target, File.join(relative, entry))
290
+ end
291
+ else
292
+ FileUtils.mkdir_p(File.dirname(destination))
293
+ FileUtils.cp(source, destination, preserve: true)
294
+ end
295
+ end
296
+
297
+ def generated_client_template_path?(relative)
298
+ components = relative.split(File::SEPARATOR)
299
+ generated_components = %w[
300
+ .build .cache .claude .dart_tool .git .idea .swiftpm .symlinks
301
+ build DerivedData Pods xcuserdata
302
+ ]
303
+ return true if components.any? { |component| generated_components.include?(component) }
304
+ return true if components.any? { |component| component.start_with?(".build-") }
305
+
306
+ generated_files = %w[
307
+ .DS_Store Package.resolved Podfile.lock local.properties
308
+ pubspec_overrides.yaml
309
+ ]
310
+ generated_files.include?(components.last) || components.last.end_with?(".xcuserstate")
311
+ end
312
+
258
313
  def write_default_ruflet_config(root, app_name)
259
314
  File.write(File.join(root, "ruflet.yaml"), <<~YAML)
260
315
  app:
@@ -313,6 +368,21 @@ module Ruflet
313
368
  end
314
369
  end
315
370
 
371
+ def copy_default_apple_extensions(root)
372
+ template_root = resolve_ruflet_client_template_root || ensure_cached_ruflet_client_template!
373
+ source = template_root && File.join(template_root, "apple_extensions")
374
+ return unless source && File.file?(File.join(source, "Package.swift"))
375
+
376
+ destination = File.join(root, "apple_extensions")
377
+ FileUtils.mkdir_p(destination)
378
+ generated_entries = %w[.build .swiftpm .dart_tool .claude]
379
+ Dir.children(source).each do |entry|
380
+ next if generated_entries.include?(entry)
381
+
382
+ FileUtils.cp_r(File.join(source, entry), File.join(destination, entry), preserve: true)
383
+ end
384
+ end
385
+
316
386
  def default_project_assets_root
317
387
  [
318
388
  File.expand_path("../../../assets", __dir__),