captive-stack-detector 0.8.3 → 0.9.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 +4 -4
- data/docs/stacks.md +35 -1
- data/lib/captive_stack_detector/env_template_scanner.rb +10 -0
- data/lib/captive_stack_detector/file_reader.rb +1 -1
- data/lib/captive_stack_detector/github_tree.rb +1 -1
- data/lib/captive_stack_detector/js_env_detector.rb +21 -0
- data/lib/captive_stack_detector/js_env_files.rb +23 -0
- data/lib/captive_stack_detector/js_env_lexer.rb +63 -0
- data/lib/captive_stack_detector/js_env_scanner.rb +22 -0
- data/lib/captive_stack_detector/js_env_syntax.rb +18 -0
- data/lib/captive_stack_detector/js_stack_detector.rb +2 -1
- data/lib/captive_stack_detector/package_json_analyzer.rb +1 -1
- data/lib/captive_stack_detector/static_site_detector.rb +31 -0
- data/lib/captive_stack_detector/version.rb +1 -1
- data/lib/captive_stack_detector.rb +5 -1
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b76909efc2f43ad9460c45c09e1d49e2129a71dd6a83e9034d3e7014d838e3e7
|
|
4
|
+
data.tar.gz: d1e3d67c81abed91d14dafdb56b025113a1dc0fdd66c426570dc13b84fc1610c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9afd81fb1afd6ddc07b72d94159aae066675f05ad4a9c8fe2934746de386cf542796162b7e834b7bddbbb2c5bdb5f8eb023c15f69c2824a5e02767e609e4d654
|
|
7
|
+
data.tar.gz: ef0f80061ab951116e7abda178814cfa864843ca924f1fb4de539169c8b3ff52892bb9844cb9e73ba5fc69b7dbd0588d3b061c74d01a1d952a052df1f0525a8e
|
data/docs/stacks.md
CHANGED
|
@@ -69,6 +69,30 @@ Pour Astro, utilisez l'adaptateur `@astrojs/node` en mode `standalone`, avec `ou
|
|
|
69
69
|
- Une ligne `worker: <commande>` dans un `Procfile` lance un worker.
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
### Variables à renseigner
|
|
73
|
+
|
|
74
|
+
La détection Node recherche les lectures à clé littérale `process.env.MA_CLE`,
|
|
75
|
+
`process.env["MA_CLE"]`, `import.meta.env.MA_CLE` et `import.meta.env["MA_CLE"]`
|
|
76
|
+
dans les sources JavaScript, TypeScript, JSX, TSX, Astro, Vue et Svelte, y compris les
|
|
77
|
+
fichiers de configuration à la racine. Les imports nommés de `astro:env/client` et
|
|
78
|
+
`astro:env/server` sont également reconnus.
|
|
79
|
+
|
|
80
|
+
Les modèles `.env.example`, `.env.sample` et `.env.template` à la racine déclarent aussi
|
|
81
|
+
des noms de variables à renseigner. **Leurs valeurs ne sont jamais reprises** : chaque nom
|
|
82
|
+
est signalé avec la valeur `placeholder`. Les fichiers `.env`, `.env.local` et les autres
|
|
83
|
+
fichiers d'environnement réels ne sont pas lus.
|
|
84
|
+
|
|
85
|
+
Les commentaires, chaînes d'exemple, dépendances, sorties de build, scripts d'outillage
|
|
86
|
+
(`scripts/`, `bin/`), documentation, exemples, tests, stories et
|
|
87
|
+
déclarations TypeScript sont ignorés. Les constantes intégrées de Vite et les variables
|
|
88
|
+
`NODE_ENV`, `NODE_OPTIONS`, `HOST` et `PORT` ne sont pas demandées. Une lecture avec une
|
|
89
|
+
valeur de repli littérale (`?? "local"`, `|| "local"`, nombre ou booléen) est facultative ;
|
|
90
|
+
une lecture sans repli ailleurs, ou sa déclaration dans un modèle, demande toujours la clé.
|
|
91
|
+
|
|
92
|
+
L'analyse est statique : les clés calculées, la déstructuration et les appels indirects
|
|
93
|
+
(comme `getSecret(nom)`) ne sont pas déduits. Déclarez ces clés dans un modèle si nécessaire.
|
|
94
|
+
La même analyse est utilisée sur les dépôts locaux et GitHub.
|
|
95
|
+
|
|
72
96
|
### Variables exposées au navigateur
|
|
73
97
|
|
|
74
98
|
Une variable n'arrive dans le code du navigateur que si elle porte le préfixe de votre bundler. Captive le déduit de vos dépendances :
|
|
@@ -77,6 +101,7 @@ Une variable n'arrive dans le code du navigateur que si elle porte le préfixe d
|
|
|
77
101
|
| --- | --- |
|
|
78
102
|
| `expo` | `EXPO_PUBLIC_` |
|
|
79
103
|
| `next` | `NEXT_PUBLIC_` |
|
|
104
|
+
| `astro` | `PUBLIC_` |
|
|
80
105
|
| `vite` | `VITE_` |
|
|
81
106
|
|
|
82
107
|
Si plusieurs sont présentes, c'est le premier de la liste qui l'emporte (`vite` est souvent là pour les tests seulement).
|
|
@@ -85,6 +110,15 @@ Avec un backend Supabase, Captive pose l'URL et la clé publique sous ce préfix
|
|
|
85
110
|
|
|
86
111
|
Ces variables sont lues **au build** : après en avoir changé une, redéployez.
|
|
87
112
|
|
|
113
|
+
### Site statique sans build
|
|
114
|
+
|
|
115
|
+
Condition : ni `Gemfile` ni `package.json` à la racine, mais un `public/index.html` ou un `index.html` à la racine. Rien n'est installé ni buildé : vos fichiers sont servis tels quels.
|
|
116
|
+
|
|
117
|
+
- Si `public/` existe, seul ce dossier est servi.
|
|
118
|
+
- Sinon la racine est servie, limitée aux fichiers web (HTML, JavaScript, CSS, JSON, images, polices, manifeste, texte). Les dossiers cachés (`.git/`, `.github/`), `supabase/`, vos scripts SQL ou votre documentation ne sont jamais publiés.
|
|
119
|
+
- Un dossier `supabase/` avec son `config.toml` provisionne un backend Supabase.
|
|
120
|
+
- Faute de build, aucune variable n'est injectée : votre code lit directement l'adresse de l'API, par exemple `location.origin`.
|
|
121
|
+
|
|
88
122
|
## Expo
|
|
89
123
|
|
|
90
124
|
Condition : un `package.json` à la racine avec la dépendance `expo` (dans `dependencies` ou `devDependencies`). Aucun script n'est exigé.
|
|
@@ -101,7 +135,7 @@ Condition : un `package.json` à la racine avec la dépendance `expo` (dans `dep
|
|
|
101
135
|
|
|
102
136
|
| Message | Cause | Que faire |
|
|
103
137
|
| --- | --- | --- |
|
|
104
|
-
| Aucun Gemfile ni package.json à la racine du dépôt. | Pas de fichier de dépendances à la racine. | Vérifiez que l'application est bien à la racine du dépôt, pas dans un sous-dossier (`app/`, `frontend/`…). Les monorepos ne sont pas pris en charge. |
|
|
138
|
+
| Aucun Gemfile ni package.json à la racine du dépôt. | Pas de fichier de dépendances ni d'`index.html` à la racine. | Vérifiez que l'application est bien à la racine du dépôt, pas dans un sous-dossier (`app/`, `frontend/`…). Les monorepos ne sont pas pris en charge. |
|
|
105
139
|
| Gemfile trouvé, mais sans la gem rails. | Application Ruby non Rails (Sinatra, Hanami…), ou `Gemfile` d'outillage dans un projet JavaScript. | Seul Rails est pris en charge côté Ruby. Si le `Gemfile` ne sert qu'à de l'outillage, retirez-le du dépôt. |
|
|
106
140
|
| package.json trouvé, mais sans dépendance expo ni script start ou build. | Captive ne sait pas comment construire l'application. | Ajoutez un script `build` (application web) ou `start` (serveur), comme dans les exemples ci-dessus. |
|
|
107
141
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveStackDetector
|
|
4
|
+
# Les modèles déclarent des noms ; leurs valeurs d'exemple ne sont jamais importées.
|
|
5
|
+
module EnvTemplateScanner
|
|
6
|
+
def self.scan(content)
|
|
7
|
+
content.scan(/^\s*(?:export\s+)?([A-Z_][A-Z0-9_]*)\s*=/).flatten.to_h { |key| [ key, "placeholder" ] }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -30,7 +30,7 @@ module CaptiveStackDetector
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def list(dir)
|
|
33
|
-
Dir.glob("#{dir}/**/*", base: @path).select { |entry| File.file?(File.join(@path, entry)) }.sort
|
|
33
|
+
Dir.glob("#{dir}/**/*", File::FNM_DOTMATCH, base: @path).select { |entry| File.file?(File.join(@path, entry)) }.sort
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "js_env_files"
|
|
4
|
+
|
|
5
|
+
module CaptiveStackDetector
|
|
6
|
+
# Ne lit que les sources et les modèles d'environnement, jamais les secrets .env.
|
|
7
|
+
class JsEnvDetector
|
|
8
|
+
def initialize(reader)
|
|
9
|
+
@reader = reader
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def detect
|
|
13
|
+
vars = @reader.list(".").sort.each_with_object({}) do |entry, found|
|
|
14
|
+
path = entry.delete_prefix("./")
|
|
15
|
+
parser = JsEnvFiles.parser(path)
|
|
16
|
+
found.merge!(parser.scan(@reader.read(path).to_s)) if parser
|
|
17
|
+
end
|
|
18
|
+
vars.reject { |key, _| JsEnvScanner::RUNTIME_VARS.include?(key) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "js_env_scanner"
|
|
4
|
+
require_relative "env_template_scanner"
|
|
5
|
+
|
|
6
|
+
module CaptiveStackDetector
|
|
7
|
+
# Choisit les fichiers lisibles avant tout téléchargement de contenu.
|
|
8
|
+
module JsEnvFiles
|
|
9
|
+
TEMPLATES = %w[.env.example .env.sample .env.template].freeze
|
|
10
|
+
EXCLUDED = %r{
|
|
11
|
+
(?:\A|/)(?:node_modules|vendor|\.git|dist|build|\.output|\.next|\.nuxt|\.astro|coverage|
|
|
12
|
+
test|tests|spec|__tests__|__mocks__|\.storybook|\.github|scripts|bin|docs|examples|fixtures|e2e)(?:/|\z)
|
|
13
|
+
}x
|
|
14
|
+
SOURCE = /(?<!\.test)(?<!\.spec)(?<!\.stories)(?<!\.d)\.(?:[cm]?[jt]sx?|astro|vue|svelte)\z/
|
|
15
|
+
|
|
16
|
+
def self.parser(path)
|
|
17
|
+
return nil if EXCLUDED.match?(path)
|
|
18
|
+
return EnvTemplateScanner if TEMPLATES.include?(path)
|
|
19
|
+
|
|
20
|
+
JsEnvScanner if SOURCE.match?(path)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "strscan"
|
|
4
|
+
require_relative "js_env_syntax"
|
|
5
|
+
|
|
6
|
+
module CaptiveStackDetector
|
|
7
|
+
# Énumère les accès littéraux en ignorant commentaires et texte non exécuté.
|
|
8
|
+
class JsEnvLexer
|
|
9
|
+
include JsEnvSyntax
|
|
10
|
+
|
|
11
|
+
def initialize(content)
|
|
12
|
+
@scanner = StringScanner.new(content, fixed_anchor: true)
|
|
13
|
+
@references = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def references
|
|
17
|
+
code
|
|
18
|
+
@references
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def code(terminator = nil)
|
|
24
|
+
until @scanner.eos?
|
|
25
|
+
next if @scanner.skip(IGNORED)
|
|
26
|
+
|
|
27
|
+
break if consume == terminator
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def consume
|
|
32
|
+
if @scanner.scan(ASTRO_IMPORT)
|
|
33
|
+
astro_import(@scanner[1])
|
|
34
|
+
elsif @scanner.scan(REFERENCE)
|
|
35
|
+
reference
|
|
36
|
+
else
|
|
37
|
+
punctuation(@scanner.getch)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def punctuation(char)
|
|
42
|
+
code("}") if char == "{"
|
|
43
|
+
template if char == "`"
|
|
44
|
+
char
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def astro_import(names)
|
|
48
|
+
JsEnvSyntax.astro_keys(names).each { |key| @references << [ key, "astro", "" ] }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def reference
|
|
52
|
+
scope = @scanner[1].start_with?("import") ? "vite" : "node"
|
|
53
|
+
@references << [ @scanner[2] || @scanner[3], scope, @scanner.rest ]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def template
|
|
57
|
+
until @scanner.eos? || @scanner.skip(/`/)
|
|
58
|
+
code("}") if @scanner.skip(/\$\{/)
|
|
59
|
+
@scanner.skip(/(?:\\.|[^`$]|\$(?!\{))+/m)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "js_env_lexer"
|
|
4
|
+
|
|
5
|
+
module CaptiveStackDetector
|
|
6
|
+
# Ne demande que les lectures sans valeur de repli et hors variables intégrées.
|
|
7
|
+
module JsEnvScanner
|
|
8
|
+
DEFAULT = /\A\s*(?:\?\?|\|\|)\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|true\b|false\b|\d+(?:\.\d+)?\b)/
|
|
9
|
+
ASSIGNMENT = /\A\s*(?:=(?!=|>)|\?\?=|\|\|=|&&=)/
|
|
10
|
+
RUNTIME_VARS = %w[NODE_ENV NODE_OPTIONS PORT HOST].freeze
|
|
11
|
+
VITE_VARS = %w[BASE_URL MODE DEV PROD SSR].freeze
|
|
12
|
+
|
|
13
|
+
def self.scan(content)
|
|
14
|
+
JsEnvLexer.new(content).references.each_with_object({}) do |(key, scope, rest), found|
|
|
15
|
+
next if RUNTIME_VARS.include?(key) || (scope == "vite" && VITE_VARS.include?(key))
|
|
16
|
+
next if DEFAULT.match?(rest) || ASSIGNMENT.match?(rest)
|
|
17
|
+
|
|
18
|
+
found[key] = "placeholder"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveStackDetector
|
|
4
|
+
# Tokens reconnus sans interpréter le programme JavaScript.
|
|
5
|
+
module JsEnvSyntax
|
|
6
|
+
KEY = /[A-Z_][A-Z0-9_]*/
|
|
7
|
+
REFERENCE = /
|
|
8
|
+
(?<![\w$.])(process\s*\.\s*env|import\s*\.\s*meta\s*\.\s*env)
|
|
9
|
+
(?:\s*\.\s*(#{KEY})(?![\w$])|\s*\[\s*['"](#{KEY})['"]\s*\])
|
|
10
|
+
/x
|
|
11
|
+
ASTRO_IMPORT = /import\s*\{([^}]+)\}\s*from\s*['"]astro:env\/(?:client|server)['"]/m
|
|
12
|
+
COMMENT = %r{//[^\n]*|/\*.*?\*/|<!--.*?-->}m
|
|
13
|
+
IGNORED = Regexp.union(COMMENT, /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/m)
|
|
14
|
+
def self.astro_keys(names)
|
|
15
|
+
names.gsub(COMMENT, "").split(",").filter_map { |name| name.strip[/\A#{KEY}\b/] }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../captive_stack_detector"
|
|
4
4
|
require_relative "package_json_analyzer"
|
|
5
|
+
require_relative "js_env_detector"
|
|
5
6
|
|
|
6
7
|
module CaptiveStackDetector
|
|
7
8
|
class JsStackDetector
|
|
@@ -20,7 +21,7 @@ module CaptiveStackDetector
|
|
|
20
21
|
services: Services.new(database: @analyzer.database, queue: @analyzer.queue, backend: @analyzer.backend),
|
|
21
22
|
worker: build_worker,
|
|
22
23
|
runtime: Runtime.new(ruby: nil, node: @reader.node_version),
|
|
23
|
-
env_vars:
|
|
24
|
+
env_vars: JsEnvDetector.new(@reader).detect,
|
|
24
25
|
system_packages: [],
|
|
25
26
|
public_env_prefix: @analyzer.public_env_prefix,
|
|
26
27
|
)
|
|
@@ -5,7 +5,7 @@ require_relative "ssr_framework_detector"
|
|
|
5
5
|
|
|
6
6
|
module CaptiveStackDetector
|
|
7
7
|
class PackageJsonAnalyzer
|
|
8
|
-
PUBLIC_ENV_PREFIXES = { "expo" => "EXPO_PUBLIC_", "next" => "NEXT_PUBLIC_", "vite" => "VITE_" }.freeze
|
|
8
|
+
PUBLIC_ENV_PREFIXES = { "expo" => "EXPO_PUBLIC_", "next" => "NEXT_PUBLIC_", "astro" => "PUBLIC_", "vite" => "VITE_" }.freeze
|
|
9
9
|
# Des clients, pas des ORM : Prisma ou Drizzle parlent aussi MySQL et SQLite, leur
|
|
10
10
|
# présence ne dit pas que l'app attend une base Postgres.
|
|
11
11
|
POSTGRES_CLIENTS = %w[pg postgres].freeze
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "types"
|
|
4
|
+
|
|
5
|
+
module CaptiveStackDetector
|
|
6
|
+
# Un site statique sans manifeste ni build (index.html qui charge ses scripts) : stack node
|
|
7
|
+
# sans subtype, servie telle quelle. Supabase se reconnaît à son dossier, faute de package.json.
|
|
8
|
+
class StaticSiteDetector
|
|
9
|
+
ENTRIES = %w[public/index.html index.html].freeze
|
|
10
|
+
|
|
11
|
+
def initialize(reader)
|
|
12
|
+
@reader = reader
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def site?
|
|
16
|
+
ENTRIES.any? { |entry| @reader.read(entry) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def detect
|
|
20
|
+
Result.new(
|
|
21
|
+
type: "node", subtype: nil, worker: nil, env_vars: [], system_packages: [],
|
|
22
|
+
services: Services.new(database: nil, queue: nil, backend: backend),
|
|
23
|
+
runtime: Runtime.new(ruby: nil, node: nil),
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def backend = ("supabase" if @reader.read("supabase/config.toml"))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -6,6 +6,7 @@ require_relative "captive_stack_detector/file_reader"
|
|
|
6
6
|
require_relative "captive_stack_detector/gemfile_analyzer"
|
|
7
7
|
require_relative "captive_stack_detector/rails_stack_detector"
|
|
8
8
|
require_relative "captive_stack_detector/js_stack_detector"
|
|
9
|
+
require_relative "captive_stack_detector/static_site_detector"
|
|
9
10
|
|
|
10
11
|
module CaptiveStackDetector
|
|
11
12
|
# Doc publique des stacks supportées, livrée dans la gem et affichée par le dashboard (ADR 0003).
|
|
@@ -23,7 +24,10 @@ module CaptiveStackDetector
|
|
|
23
24
|
return RailsStackDetector.new(reader, GemfileAnalyzer.new(gemfile)).detect if gemfile
|
|
24
25
|
return JsStackDetector.new(reader, package_json).detect if package_json
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
static_site = StaticSiteDetector.new(reader)
|
|
28
|
+
raise UnsupportedStack, :no_manifest unless static_site.site?
|
|
29
|
+
|
|
30
|
+
static_site.detect
|
|
27
31
|
end
|
|
28
32
|
private_class_method :detect_from
|
|
29
33
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: captive-stack-detector
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Captive Studio
|
|
@@ -21,6 +21,7 @@ files:
|
|
|
21
21
|
- lib/captive/stack/detector.rb
|
|
22
22
|
- lib/captive_stack_detector.rb
|
|
23
23
|
- lib/captive_stack_detector/aptfile_parser.rb
|
|
24
|
+
- lib/captive_stack_detector/env_template_scanner.rb
|
|
24
25
|
- lib/captive_stack_detector/env_vars_scanner.rb
|
|
25
26
|
- lib/captive_stack_detector/file_content_parser.rb
|
|
26
27
|
- lib/captive_stack_detector/file_reader.rb
|
|
@@ -28,6 +29,11 @@ files:
|
|
|
28
29
|
- lib/captive_stack_detector/gemfile_analyzer.rb
|
|
29
30
|
- lib/captive_stack_detector/github_api_client.rb
|
|
30
31
|
- lib/captive_stack_detector/github_tree.rb
|
|
32
|
+
- lib/captive_stack_detector/js_env_detector.rb
|
|
33
|
+
- lib/captive_stack_detector/js_env_files.rb
|
|
34
|
+
- lib/captive_stack_detector/js_env_lexer.rb
|
|
35
|
+
- lib/captive_stack_detector/js_env_scanner.rb
|
|
36
|
+
- lib/captive_stack_detector/js_env_syntax.rb
|
|
31
37
|
- lib/captive_stack_detector/js_stack_detector.rb
|
|
32
38
|
- lib/captive_stack_detector/node_version_detector.rb
|
|
33
39
|
- lib/captive_stack_detector/package_json_analyzer.rb
|
|
@@ -37,6 +43,7 @@ files:
|
|
|
37
43
|
- lib/captive_stack_detector/solid_queue_usage.rb
|
|
38
44
|
- lib/captive_stack_detector/solid_queue_worker.rb
|
|
39
45
|
- lib/captive_stack_detector/ssr_framework_detector.rb
|
|
46
|
+
- lib/captive_stack_detector/static_site_detector.rb
|
|
40
47
|
- lib/captive_stack_detector/system_package_detector.rb
|
|
41
48
|
- lib/captive_stack_detector/types.rb
|
|
42
49
|
- lib/captive_stack_detector/version.rb
|