proscenium 0.26.0-aarch64-linux-gnu → 0.27.0-aarch64-linux-gnu

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +50 -3
  3. data/lib/proscenium/app_package.rb +8 -4
  4. data/lib/proscenium/builder/request.rb +72 -0
  5. data/lib/proscenium/builder.rb +10 -74
  6. data/lib/proscenium/cli/bun_linker.rb +2 -2
  7. data/lib/proscenium/cli/collisions.rb +7 -9
  8. data/lib/proscenium/cli/error.rb +58 -265
  9. data/lib/proscenium/cli/gem_archive.rb +4 -2
  10. data/lib/proscenium/cli/gem_check.rb +13 -11
  11. data/lib/proscenium/cli/gitignore.rb +2 -1
  12. data/lib/proscenium/cli/inspect.rb +12 -11
  13. data/lib/proscenium/cli/install.rb +35 -46
  14. data/lib/proscenium/cli/manager.rb +6 -6
  15. data/lib/proscenium/cli/platform_variants.rb +8 -8
  16. data/lib/proscenium/cli/pnpm_approvals.rb +3 -3
  17. data/lib/proscenium/cli/pnpm_workspace.rb +15 -14
  18. data/lib/proscenium/cli/project_lock.rb +3 -3
  19. data/lib/proscenium/cli/registration.rb +5 -5
  20. data/lib/proscenium/cli/reporter.rb +2 -2
  21. data/lib/proscenium/cli/verify.rb +9 -50
  22. data/lib/proscenium/cli.rb +5 -4
  23. data/lib/proscenium/contexts/map.rb +183 -0
  24. data/lib/proscenium/contexts/mapping_generation.rb +68 -0
  25. data/lib/proscenium/contexts/messages.rb +276 -0
  26. data/lib/proscenium/{cli/contexts.rb → contexts/plan.rb} +12 -12
  27. data/lib/proscenium/contexts/projection.rb +77 -0
  28. data/lib/proscenium/{cli → contexts}/rules.rb +1 -1
  29. data/lib/proscenium/contexts/stale.rb +174 -0
  30. data/lib/proscenium/contexts.rb +16 -0
  31. data/lib/proscenium/css_module/path.rb +1 -0
  32. data/lib/proscenium/css_module/transformer.rb +15 -0
  33. data/lib/proscenium/css_module.rb +3 -2
  34. data/lib/proscenium/ext/proscenium +0 -0
  35. data/lib/proscenium/helper.rb +6 -1
  36. data/lib/proscenium/importer.rb +13 -1
  37. data/lib/proscenium/middleware/base.rb +0 -4
  38. data/lib/proscenium/middleware.rb +0 -2
  39. data/lib/proscenium/railtie.rb +4 -4
  40. data/lib/proscenium/resolver.rb +3 -1
  41. data/lib/proscenium/side_load.rb +6 -2
  42. data/lib/proscenium/source_path.rb +2 -0
  43. data/lib/proscenium/utils.rb +2 -1
  44. data/lib/proscenium/version.rb +1 -1
  45. data/lib/proscenium.rb +5 -5
  46. metadata +10 -7
  47. data/lib/proscenium/context_map.rb +0 -181
  48. data/lib/proscenium/dependency_context.rb +0 -74
  49. data/lib/proscenium/mapping_generation.rb +0 -64
  50. data/lib/proscenium/stale_contexts.rb +0 -118
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b418aa535b16ab055b1b80a8ad51cc2d40417fbef0b95b445eae7df37e302b10
4
- data.tar.gz: 655a3615572bd0c6b21b048514b3de96afeca059c13ce05811d16c9c9244a300
3
+ metadata.gz: 38d676f1a0fad15c96c7e6de0ceb61c0380ab55ea187ece074ff8f943d8d9ae6
4
+ data.tar.gz: f25b2186a86c307ac22c5f77ce50614d3b463f262ceb61a77a4a86481b6c47f3
5
5
  SHA512:
6
- metadata.gz: b5a713ed93396c3db4da692de6b4bbe926b489496c01a8f8cb1b12754824567c7f5465c90c34cf591a5ed17a33b3f6df6b8178a44d129f9257367ce383a151df
7
- data.tar.gz: 61a62b4333f9d47cff2f3b93f645d92c4ca4ccc3a5bf5db83b196ee6a088f4224ff0cedcc740c9963259c08438e3f85fb0dd4739219266d377c13e56eb5efedb
6
+ metadata.gz: 44add4f943e1fa46f7a40bd7bb7b24013d9304467b1c719f2bce461bd92fb42fa64567ea0c1d76bb403b77462c321ce0208b292aa62cc736af79dc83c8a98ade
7
+ data.tar.gz: ccfaa537ebbd8368e13b1a5998ab6d119d3b61db24d092eb3f81cb5d5240805cc01655ca715dd260ec0445d078925d4ea2ed5c2c6e34842237b52bb6f4285d22
data/README.md CHANGED
@@ -56,6 +56,7 @@
56
56
  - [Resolution](#resolution)
57
57
  - [Aliases](#aliases)
58
58
  - [Pre-compilation](#pre-compilation)
59
+ - [Extending Proscenium](#extending-proscenium)
59
60
  - [Puma `preload_app!` and Cluster Mode](#puma-preload_app-and-cluster-mode)
60
61
  - [Thanks](#thanks)
61
62
  - [Development](#development)
@@ -103,7 +104,8 @@ fine; if not, `gem update --system`. Bundler 2.5.6 or newer resolves the platfor
103
104
  needing anything else from you.
104
105
 
105
106
  Installing on a platform not listed here gets you the platform-less gem, which carries no compiled
106
- library, and Proscenium will say so by name when it loads rather than failing somewhere inside FFI.
107
+ library, and Proscenium will say so by name on its first build or resolve, rather than failing
108
+ somewhere inside FFI.
107
109
  If your platform should be supported, please [open an
108
110
  issue](https://github.com/joelmoss/proscenium/issues).
109
111
 
@@ -742,6 +744,8 @@ p {
742
744
 
743
745
  A mixin must be defined at the root of a file. A `@define-mixin` inside a rule or an at-rule block such as `@media`, or inside another mixin, does not define anything: it is passed through as written, with a warning.
744
746
 
747
+ A mixin from another file whose definition is malformed is refused: its `@mixin` is left as written, with a "malformed CSS" warning, and the stylesheet's other mixins still expand. A definition is malformed when it holds a bad token, such as a string broken by a newline, or when the file ends before closing it, inside an open comment, `url(` or escape (as when it is saved mid-edit). Malformed CSS in the stylesheet itself is returned as written, for esbuild to report.
748
+
745
749
  One stylesheet may expand at most 100,000 mixins, inserting at most 4 MiB (4,194,304 bytes) of mixin definitions, with mixins nested at most 100 deep, so a chain of mixins that each include another more than once cannot grow without bound. Past any limit, each remaining `@mixin` is left as written, with one warning.
746
750
 
747
751
  Each stylesheet reports at most 100 warnings, plus one saying how many more were not reported and, past a limit, the mixin limit warning. A precompile sends at most 100 errors and 100 warnings in all, then one more message for each saying how many more there were, so in a build that noisy the limit warning can be one of those counted.
@@ -909,10 +913,12 @@ treated as test, and so is served and precompiled unminified.
909
913
 
910
914
  ## Resolution
911
915
 
912
- Proscenium will serve files ending with any of these extension: `js,mjs,ts,css,jsx,tsx` from the following directories, and their sub-directories of your Rails application's root: `/app`, `/lib`, `/config`, `/node_modules`, `/vendor`.
916
+ Proscenium will build and serve files ending with any of these extension: `js,mjs,ts,css,jsx,tsx` from the following directories, and their sub-directories of your Rails application's root: `/app`, `/lib`, `/config`, `/node_modules`.
913
917
 
914
918
  So a file at `/app/views/users/index.js` will be served from `https://localhost:3000/app/views/users/index.js`.
915
919
 
920
+ `.js` and `.css` files under `/vendor` are served as they are, without building, and with `Cache-Control: public, max-age=3155695200, immutable`. A browser never asks for one again, so put a version in the path of each vendored file (`/vendor/foo/1.2.3/foo.js`), and change the path when the file changes. This suits a package you load at run time rather than bundle, such as one marked `external`.
921
+
916
922
  You can continue to access any file in the `/public` directory as you normally would. Proscenium will not process files in the `/public` directory.
917
923
 
918
924
  If requesting a file that exists in a root directory and the public directory, the file in the public directory will be served. For example, if you have a file at `/lib/foo.js` and `/public/lib/foo.js`, and you request `/lib/foo.js`, the file in the public directory (`/public/lib/foo.js`) will be served.
@@ -959,11 +965,52 @@ Rails.configuration.proscenium.precompile = Set[
959
965
 
960
966
  This will bundle, code split, tree shake, and compile all your JS, TS, JSX, TSX and CSS files and place them in the `public/assets` directory, ready to be served in production.
961
967
 
968
+ ## Extending Proscenium
969
+
970
+ Gems that bring side loading and CSS modules to other component libraries, such as
971
+ [proscenium-phlex](https://github.com/joelmoss/proscenium-phlex) and
972
+ [proscenium-view_component](https://github.com/joelmoss/proscenium-view_component), build on a
973
+ small public API. Each method below is tagged `@api public` and has its own tests, so it changes
974
+ only with a note in the release.
975
+
976
+ | API | What it does |
977
+ | --- | --- |
978
+ | `Proscenium::SideLoad.sideload_inheritance_chain(obj, options)` | Side loads the assets of `obj`'s class and each superclass, as described below. |
979
+ | `Proscenium::SideLoad.merge_options(base, override, receiver)` | Merges side load options as Proscenium does for views: a new Hash, keys symbolized at every depth, any `css`/`js` Proc evaluated against `receiver`. |
980
+ | `Proscenium::Importer.import(path, sideloaded: false, **options)` | Imports one asset for the current request, once, merging repeat options (later ones win). A truthy `sideloaded` sends a `sideload.proscenium` notification. |
981
+ | `Proscenium::Importer.each_stylesheet(delete: false)`, `each_javascript(delete: false)` | Yield each imported stylesheet or script with its options, for an app that writes its own tags. `delete: true` also removes each one from the imports, so it is written only once. |
982
+ | `Proscenium::Importer.sideload(path, **options)` | Imports the script and stylesheet beside the Ruby file at `path`, an absolute `Pathname` (anything else raises `ArgumentError`). Does nothing when side loading is off, or `js:` and `css:` are both `false`. `lazy: true` imports a script but writes no tag for it, for a component that loads it itself. |
983
+ | `Proscenium::CssModule` | Included into a component, against its `css_module_path`: `css_module` transforms every name, `class_names` only `@`-prefixed ones and paths (a name with a `/`, such as `/lib/users@name`), and the private `cssm` is the component's `Transformer`. |
984
+ | `Proscenium::CssModule::Path` | Extended by a component class: `css_module_path` is the `.module.css` beside its source file, else the nearest ancestor's. |
985
+ | `Proscenium::CssModule::Transformer#class_names(*names, require_prefix: true)` | Transforms names; given a block, yields each transformed name and the path of the CSS module it side loads, if any. |
986
+ | `Proscenium::CssModule::Transformer#class_name!(name, original_name, path: source_path)` | Transforms one name, whatever its prefix, against the CSS module at `path`, and side loads that module. Raises `TransformError`, naming `original_name`, when there is no path. |
987
+ | `Proscenium::SourcePath` | Included into a class: `source_path` is the file the class is defined in. |
988
+ | `Proscenium::Resolver.resolve(path)` | Where an asset is served, for an absolute file or URL path, or a bare specifier such as an NPM package (a `./` or `../` path raises `ArgumentError`): its URL, or, when a loaded manifest has it, the entry's outputs as an Array, its own first. `as_array: true` returns `[manifest outputs, URL, file]`. |
989
+ | `Proscenium::Utils.merge_bang_attributes!(names, attributes, allowed)` | Turns an allowed `:name!` argument into `name: true`, unless `name` was given as a keyword. |
990
+
991
+ Side loading and CSS module lookup walk a component's class hierarchy through these methods,
992
+ which the component's classes provide:
993
+
994
+ - `source_path` on each class: the Ruby file whose sibling `.js`/`.ts`/`.jsx`/`.tsx`, `.css` and
995
+ `.module.css` files are side loaded.
996
+ - `abstract_class` on each class, optional: when true, the walk stops there, and neither that
997
+ class nor any above it is side loaded or searched for a CSS module.
998
+ - `sideload(options)` on each class, optional: called after that class's own assets, to side load
999
+ anything else.
1000
+ - `sideload_assets_options` on the instance: its own side load options, merged over the ones
1001
+ given.
1002
+ - `css_module_path` on each class, from `CssModule::Path`: the class's CSS module, used by
1003
+ `CssModule` and walked the same way, ending at an abstract class.
1004
+
1005
+ The walk side loads each class's script, starting with the component's own, then the first CSS
1006
+ module found, then each class's stylesheet starting with the topmost, so a component's styles
1007
+ come after the ones it inherits.
1008
+
962
1009
  ## Puma `preload_app!` and Cluster Mode
963
1010
 
964
1011
  Proscenium's builder is backed by a Go shared library, and Go's runtime has a known, unfixed limitation ([golang/go#15538](https://github.com/golang/go/issues/15538)): if the Go runtime has already been initialized in a process before that process calls `fork()`, the forked child's Go runtime is left in a broken state (only the forking thread survives `fork()`; the Go scheduler and GC's other threads simply vanish) and any subsequent call into Go code in that child can hang or fail.
965
1012
 
966
- This matters if you run Puma in cluster mode with `preload_app!` (the app, including gems, is booted once in the master process, then workers are created via `fork()` with no `exec()` afterward). Proscenium itself never triggers this - simply requiring the gem does not initialize the Go runtime, and nothing in Proscenium's own boot sequence calls into it. The Go runtime only initializes lazily, the first time something actually calls a builder method (`build_to_string`, `resolve`, or `compile`).
1013
+ This matters if you run Puma in cluster mode with `preload_app!` (the app, including gems, is booted once in the master process, then workers are created via `fork()` with no `exec()` afterward). Proscenium itself never triggers this - requiring the gem does not initialize the Go runtime, and neither does naming `Proscenium::Builder` or one of its error classes (so a `rescue_from Proscenium::Builder::BuildError` in a controller is safe), and nothing in Proscenium's own boot sequence calls into it. The library is opened, and the Go runtime initialized, the first time something actually calls a builder method (`build_to_string`, `resolve`, or `compile`).
967
1014
 
968
1015
  **Do not call any `Proscenium::Builder` method (directly, or indirectly via the resolver/side-loading) from a Rails initializer or any other code that runs during application boot**, if you use `preload_app!` with `workers`. Doing so initializes the Go runtime in the master process before the fork, and every worker will inherit a broken one. There is no fix available from Go's side - this is a fundamental fork() limitation, not a bug Proscenium can work around. Asset builds and resolves triggered by actual HTTP requests (the normal case) are unaffected, since those always happen after the fork, independently in each worker.
969
1016
 
@@ -3,10 +3,14 @@
3
3
  require 'json'
4
4
 
5
5
  module Proscenium
6
- # What the app's own package.json says that the context map needs (#154). Like ContextMap, which
7
- # it reads JSON through, it loads nothing the `proscenium` CLI may not.
6
+ # What the app's own package.json says that the context map needs (#154). Like Contexts::Map,
7
+ # which it reads JSON through, it loads nothing the `proscenium` CLI may not.
8
8
  module AppPackage
9
9
  LOCAL_SPECS = %w[link: file: workspace: portal:].freeze
10
+ # The fields an app's own dependencies are under. Peers too: pnpm installs the app's own peers
11
+ # at the root.
12
+ DEPENDENCY_FIELDS = %w[dependencies devDependencies optionalDependencies
13
+ peerDependencies].freeze
10
14
 
11
15
  module_function
12
16
 
@@ -18,7 +22,7 @@ module Proscenium
18
22
  package = read(root) or return []
19
23
 
20
24
  members = bun?(root, package) ? workspace_members(root, package['workspaces']) : []
21
- %w[dependencies devDependencies optionalDependencies peerDependencies].flat_map do |field|
25
+ DEPENDENCY_FIELDS.flat_map do |field|
22
26
  deps = package[field]
23
27
  next [] unless deps.is_a?(Hash)
24
28
 
@@ -81,7 +85,7 @@ module Proscenium
81
85
  # The app's package.json as an object, or nil when it is missing or not one.
82
86
  def read(root)
83
87
  path = File.join(root, 'package.json')
84
- package = File.exist?(path) && ContextMap.read_json(path)
88
+ package = File.exist?(path) && Contexts::Map.read_json(path)
85
89
  package if package.is_a?(Hash)
86
90
  rescue JSON::ParserError
87
91
  nil
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module Proscenium
6
+ class Builder
7
+ module Request
8
+ extend FFI::Library
9
+
10
+ raise UnsupportedPlatform, LIBRARY_PATH unless LIBRARY_PATH.exist?
11
+
12
+ ffi_lib LIBRARY_PATH.to_s
13
+
14
+ # Windows unmaps the library at interpreter exit, and the Go runtime does not survive it.
15
+ #
16
+ # ffi's library_free calls dl_close on every platform but macOS, which is FreeLibrary here,
17
+ # and Ruby runs every T_DATA free during VM teardown. Go's c-shared runtime cannot be
18
+ # unloaded: its threads are still running when the mapping goes. On Linux this never bites,
19
+ # because the Go linker marks the library nodelete and dlclose is a no-op; Windows has no
20
+ # equivalent. Measured on windows-latest: three of five plain `require` runs exited 139,
21
+ # while five of five exited 0 when the script ended in `exit!`, which skips the teardown.
22
+ #
23
+ # Pinning the module means the reference count never reaches zero, so FreeLibrary returns
24
+ # without unmapping anything. Nothing here needs the handle, only the pin.
25
+ if Gem.win_platform?
26
+ module Kernel32
27
+ extend FFI::Library
28
+
29
+ ffi_lib 'kernel32'
30
+ attach_function :GetModuleHandleExW, %i[uint32 buffer_in pointer], :int
31
+ end
32
+
33
+ # GET_MODULE_HANDLE_EX_FLAG_PIN. Backslashes, because GetModuleHandle documents that a full
34
+ # path must use them, and LIBRARY_PATH is built from __dir__, which uses "/". Checked,
35
+ # because a pin that quietly failed would bring back an exit crash that only happens some
36
+ # of the time, with nothing to say why.
37
+ name = "#{LIBRARY_PATH.to_s.tr('/', '\\')}\0".encode(Encoding::UTF_16LE)
38
+ if Kernel32.GetModuleHandleExW(0x1, name, FFI::MemoryPointer.new(:pointer)).zero?
39
+ warn "Proscenium could not pin #{LIBRARY_PATH} (Windows error " \
40
+ "#{FFI::LastError.winapi_error}), so Ruby may crash when it exits."
41
+ end
42
+ end
43
+
44
+ enum :environment, [:development, 1, :test, :production]
45
+
46
+ # `blocking: true` releases the GVL for the duration of the call, so a build/resolve
47
+ # doesn't stall unrelated Ruby threads (eg. other requests in a multi-threaded server).
48
+ # Safe to do because the Go side shares nothing between calls: each call parses its own
49
+ # config, and the caches that outlive a call (i18n, svg, npm replacements) carry their own
50
+ # locks. See the note in main.go. There is no serialising mutex any more; a new piece of
51
+ # global state on the Go side needs its own.
52
+
53
+ attach_function :build_to_string, [
54
+ :string, # Path or entry point.
55
+ :pointer # Config as JSON.
56
+ ], Result.by_value, blocking: true
57
+
58
+ attach_function :resolve, [
59
+ :string, # path or entry point
60
+ :pointer # Config as JSON.
61
+ ], ResolveResult.by_value, blocking: true
62
+
63
+ attach_function :compile, [
64
+ :pointer # Config as JSON.
65
+ ], CompileResult.by_value, blocking: true
66
+
67
+ attach_function :reset_config, [], :void, blocking: true
68
+
69
+ attach_function :free_cstr, [:pointer], :void
70
+ end
71
+ end
72
+ end
@@ -55,70 +55,11 @@ module Proscenium
55
55
  end
56
56
  end
57
57
 
58
- module Request
59
- extend FFI::Library
60
-
61
- raise UnsupportedPlatform, LIBRARY_PATH unless LIBRARY_PATH.exist?
62
-
63
- ffi_lib LIBRARY_PATH.to_s
64
-
65
- # Windows unmaps the library at interpreter exit, and the Go runtime does not survive it.
66
- #
67
- # ffi's library_free calls dl_close on every platform but macOS, which is FreeLibrary here,
68
- # and Ruby runs every T_DATA free during VM teardown. Go's c-shared runtime cannot be
69
- # unloaded: its threads are still running when the mapping goes. On Linux this never bites,
70
- # because the Go linker marks the library nodelete and dlclose is a no-op; Windows has no
71
- # equivalent. Measured on windows-latest: three of five plain `require` runs exited 139,
72
- # while five of five exited 0 when the script ended in `exit!`, which skips the teardown.
73
- #
74
- # Pinning the module means the reference count never reaches zero, so FreeLibrary returns
75
- # without unmapping anything. Nothing here needs the handle, only the pin.
76
- if Gem.win_platform?
77
- module Kernel32
78
- extend FFI::Library
79
-
80
- ffi_lib 'kernel32'
81
- attach_function :GetModuleHandleExW, %i[uint32 buffer_in pointer], :int
82
- end
83
-
84
- # GET_MODULE_HANDLE_EX_FLAG_PIN. Backslashes, because GetModuleHandle documents that a full
85
- # path must use them, and LIBRARY_PATH is built from __dir__, which uses "/". Checked,
86
- # because a pin that quietly failed would bring back an exit crash that only happens some
87
- # of the time, with nothing to say why.
88
- name = "#{LIBRARY_PATH.to_s.tr('/', '\\')}\0".encode(Encoding::UTF_16LE)
89
- if Kernel32.GetModuleHandleExW(0x1, name, FFI::MemoryPointer.new(:pointer)).zero?
90
- warn "Proscenium could not pin #{LIBRARY_PATH} (Windows error " \
91
- "#{FFI::LastError.winapi_error}), so Ruby may crash when it exits."
92
- end
93
- end
94
-
95
- enum :environment, [:development, 1, :test, :production]
96
-
97
- # `blocking: true` releases the GVL for the duration of the call, so a build/resolve
98
- # doesn't stall unrelated Ruby threads (eg. other requests in a multi-threaded server).
99
- # Safe to do because the Go side shares nothing between calls: each call parses its own
100
- # config, and the caches that outlive a call (i18n, svg, npm replacements) carry their own
101
- # locks. See the note in main.go. There is no serialising mutex any more; a new piece of
102
- # global state on the Go side needs its own.
103
-
104
- attach_function :build_to_string, [
105
- :string, # Path or entry point.
106
- :pointer # Config as JSON.
107
- ], Result.by_value, blocking: true
108
-
109
- attach_function :resolve, [
110
- :string, # path or entry point
111
- :pointer # Config as JSON.
112
- ], ResolveResult.by_value, blocking: true
113
-
114
- attach_function :compile, [
115
- :pointer # Config as JSON.
116
- ], CompileResult.by_value, blocking: true
117
-
118
- attach_function :reset_config, [], :void, blocking: true
119
-
120
- attach_function :free_cstr, [:pointer], :void
121
- end
58
+ # The FFI binding to the Go library. Autoloaded, so the library is opened (and the Go runtime
59
+ # started) by the first call into it, never by naming Builder or one of its error classes: a
60
+ # `rescue_from Proscenium::Builder::BuildError` in a controller loaded during a Puma
61
+ # `preload_app!` boot must not start Go before the fork (golang/go#15538).
62
+ autoload :Request, File.expand_path('builder/request', __dir__)
122
63
 
123
64
  class BuildError < Error
124
65
  attr_reader :error, :path
@@ -199,12 +140,12 @@ module Proscenium
199
140
  # finished, or the committed gem dependency contexts are stale. With
200
141
  # PROSCENIUM_STALE_CONTEXT=warn, for an incident, stale contexts are logged once instead.
201
142
  def self.refusal(root = Rails.root)
202
- project = ContextMap.project_root
203
- return ContextMap::INSTALLING_MESSAGE if ContextMap.installing?(project)
143
+ project = Contexts::Map.project_root
144
+ return Contexts::Map::INSTALLING_MESSAGE if Contexts::Map.installing?(project)
204
145
 
205
146
  outside = outside_rails_root(project, root) and return outside
206
147
 
207
- stale = StaleContexts.message(project, generation) or return
148
+ stale = Contexts::Stale.message(project, generation) or return
208
149
  return stale unless ENV['PROSCENIUM_STALE_CONTEXT'] == 'warn'
209
150
 
210
151
  @warned ||= Set.new
@@ -217,7 +158,7 @@ module Proscenium
217
158
  # has no URL the engine serves, so such a layout is refused plainly, not left to fail
218
159
  # mid-page. Only compares paths unless they differ.
219
160
  def self.outside_rails_root(project, root = Rails.root)
220
- return if within?(project, root) || !ContextMap.adopted?(project)
161
+ return if within?(project, root) || !Contexts::Map.adopted?(project)
221
162
 
222
163
  "Gem dependency contexts are under #{project}, outside the Rails app at #{root}, " \
223
164
  'which Proscenium cannot serve yet. Keep the Gemfile in the Rails root to use them.'
@@ -270,7 +211,6 @@ module Proscenium
270
211
  config_hash = {
271
212
  RootPath: (@root || Rails.root).to_s,
272
213
  OutputDir: "public#{Proscenium.config.output_dir}",
273
- GemPath: gem_root,
274
214
  Environment: ENVIRONMENTS.fetch(Rails.env.to_sym, 2),
275
215
  EnvVars: env_vars,
276
216
  CodeSplitting: Proscenium.config.code_splitting,
@@ -280,7 +220,7 @@ module Proscenium
280
220
  External: Proscenium.config.external,
281
221
  Precompile: Proscenium.config.precompile,
282
222
  Debug: Proscenium.config.debug,
283
- **ContextMap.config(ContextMap.project_root, generation)
223
+ **Contexts::Map.config(Contexts::Map.project_root, generation)
284
224
  }.merge(@overrides)
285
225
 
286
226
  self.class.request_config_pointer(config_hash)
@@ -395,9 +335,5 @@ module Proscenium
395
335
  ENV['NODE_ENV'] = ENV.fetch('RAILS_ENV', nil)
396
336
  ENV.slice(*Proscenium::DEFAULT_ENV_VARS, *names)
397
337
  end
398
-
399
- def gem_root
400
- Pathname.new(__dir__).join('..', '..').to_s
401
- end
402
338
  end
403
339
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require_relative '../context_map'
4
+ require_relative '../contexts/map'
5
5
  require_relative 'manager'
6
6
 
7
7
  module Proscenium
@@ -65,7 +65,7 @@ module Proscenium
65
65
 
66
66
  def workspaces?(root)
67
67
  path = File.join(root, 'package.json')
68
- File.exist?(path) && ContextMap.read_json(path).key?('workspaces')
68
+ File.exist?(path) && Contexts::Map.read_json(path).key?('workspaces')
69
69
  rescue JSON::ParserError
70
70
  false
71
71
  end
@@ -2,7 +2,8 @@
2
2
 
3
3
  require 'json'
4
4
  require 'yaml'
5
- require_relative 'registration'
5
+ require_relative '../app_package'
6
+ require_relative '../contexts/map'
6
7
 
7
8
  module Proscenium
8
9
  module CLI
@@ -12,14 +13,11 @@ module Proscenium
12
13
  module Collisions
13
14
  module_function
14
15
 
15
- DEPENDENCY_FIELDS = %w[dependencies devDependencies optionalDependencies
16
- peerDependencies].freeze
17
-
18
16
  # One line for each package of the app's own taking a context's name.
19
17
  def find(root, manager, gems)
20
18
  names = gems.to_h { ["@rubygems/#{it}", it] }
21
19
  app = read_json(File.join(root, 'package.json')) || {}
22
- pinned = DEPENDENCY_FIELDS.flat_map do |field|
20
+ pinned = AppPackage::DEPENDENCY_FIELDS.flat_map do |field|
23
21
  (app[field] || {}).filter_map do |name, spec|
24
22
  next if !names.key?(name) || context_workspace?(spec, name) ||
25
23
  context_link?(root, spec, names[name])
@@ -46,7 +44,7 @@ module Proscenium
46
44
  def context_link?(root, spec, gem)
47
45
  return false unless spec.to_s.start_with?('link:')
48
46
 
49
- context = File.join(File.expand_path(root), ContextMap::CONTEXTS, gem)
47
+ context = File.join(File.expand_path(root), Contexts::Map::CONTEXTS, gem)
50
48
  File.expand_path(spec.to_s.delete_prefix('link:'), root) == context
51
49
  end
52
50
 
@@ -55,7 +53,7 @@ module Proscenium
55
53
  # Bun read `./packages/*` and `packages/*` alike.
56
54
  def workspace_packages(root, manager, app)
57
55
  excluded, included = workspace_patterns(root, manager, app)
58
- .grep(String).reject { ContextMap.registration?(it) }
56
+ .grep(String).reject { Contexts::Map.registration?(it) }
59
57
  .map { it.sub(%r{\A(!?)\./}, '\\1') }
60
58
  .partition { it.start_with?('!') }
61
59
  files = included.flat_map { Dir.glob(File.join(it, 'package.json'), base: root) }.uniq
@@ -77,14 +75,14 @@ module Proscenium
77
75
  Array(workspaces.is_a?(Hash) ? workspaces['packages'] : workspaces)
78
76
  else
79
77
  path = File.join(root, 'pnpm-workspace.yaml')
80
- File.exist?(path) ? Array(ContextMap.yaml_packages(File.read(path))) : []
78
+ File.exist?(path) ? Array(Contexts::Map.yaml_packages(File.read(path))) : []
81
79
  end
82
80
  rescue Psych::SyntaxError
83
81
  [] # the manager reports it
84
82
  end
85
83
 
86
84
  def read_json(path)
87
- json = File.exist?(path) && ContextMap.read_json(path)
85
+ json = File.exist?(path) && Contexts::Map.read_json(path)
88
86
  json.is_a?(Hash) ? json : nil
89
87
  rescue JSON::ParserError
90
88
  nil