shipit-cli 0.19.6.dev1__tar.gz → 0.19.7__tar.gz

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 (48) hide show
  1. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/PKG-INFO +1 -1
  2. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/pyproject.toml +1 -1
  3. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/assets/wordpress/install.sh +11 -15
  4. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/node_static.py +338 -39
  5. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/wordpress.py +5 -5
  6. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/runners/wasmer.py +20 -18
  7. shipit_cli-0.19.7/src/shipit/version.py +5 -0
  8. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_e2e.py +49 -0
  9. shipit_cli-0.19.6.dev1/src/shipit/version.py +0 -5
  10. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/.gitignore +0 -0
  11. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/README.md +0 -0
  12. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/__init__.py +0 -0
  13. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/assets/php/php.ini +0 -0
  14. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/assets/wordpress/.htaccess +0 -0
  15. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/assets/wordpress/start.php +0 -0
  16. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/assets/wordpress/wp-config.php +0 -0
  17. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/builders/__init__.py +0 -0
  18. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/builders/base.py +0 -0
  19. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/builders/docker.py +0 -0
  20. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/builders/local.py +0 -0
  21. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/cli.py +0 -0
  22. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/generator.py +0 -0
  23. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/procfile.py +0 -0
  24. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/base.py +0 -0
  25. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/go.py +0 -0
  26. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/hugo.py +0 -0
  27. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/jekyll.py +0 -0
  28. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/laravel.py +0 -0
  29. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/mkdocs.py +0 -0
  30. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/php.py +0 -0
  31. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/python.py +0 -0
  32. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/registry.py +0 -0
  33. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/providers/staticfile.py +0 -0
  34. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/runners/__init__.py +0 -0
  35. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/runners/base.py +0 -0
  36. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/runners/local.py +0 -0
  37. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/shipit_types.py +0 -0
  38. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/ui.py +0 -0
  39. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/utils.py +0 -0
  40. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/src/shipit/volumes.py +0 -0
  41. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_cli_after_deploy.py +0 -0
  42. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_generate_shipit_examples.py +0 -0
  43. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_php_provider.py +0 -0
  44. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_staticfile_provider.py +0 -0
  45. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_version.py +0 -0
  46. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_volumes.py +0 -0
  47. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_wasmer_annotations.py +0 -0
  48. {shipit_cli-0.19.6.dev1 → shipit_cli-0.19.7}/tests/test_wordpress_phpix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shipit-cli
3
- Version: 0.19.6.dev1
3
+ Version: 0.19.7
4
4
  Summary: Shipit CLI is the best way to build, serve and deploy your projects anywhere.
5
5
  Project-URL: homepage, https://wasmer.io
6
6
  Project-URL: repository, https://github.com/wasmerio/shipit
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "shipit-cli"
3
- version = "0.19.6.dev1"
3
+ version = "0.19.7"
4
4
  description = "Shipit CLI is the best way to build, serve and deploy your projects anywhere."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -11,10 +11,6 @@ WP_LOCALE=${WP_LOCALE:-"en_US"}
11
11
  WP_SITEURL=${WP_SITEURL:-"http://localhost"}
12
12
  WP_SITE_TITLE=${WP_SITE_TITLE:-"WordPress"}
13
13
 
14
- wp() {
15
- php /opt/assets/wp-cli.phar --allow-root --path=/app "$@"
16
- }
17
-
18
14
  if wp core is-installed; then
19
15
  echo "🚀 Setting up WordPress from an existing installation"
20
16
  if [ "${WP_UPDATE_DB:-true}" = "true" ]; then
@@ -30,17 +26,17 @@ else
30
26
  echo "📁 Initializing wp-content..."
31
27
 
32
28
  mkdir -p wp-content/plugins
33
- # mkdir -p wp-content/themes
29
+ mkdir -p wp-content/themes
34
30
  mkdir -p wp-content/upgrade
35
31
 
36
- # if [ -n "${WPCONTENT_BASE_PATH:-}" ] && [ -d "${WPCONTENT_BASE_PATH}" ]; then
37
- # shopt -s dotglob nullglob
38
- # # Note: change this back to copy all, once using the WP Zip files
39
- # # cp -R "${WPCONTENT_BASE_PATH}"/* /app/wp-content
40
- # cp -R "${WPCONTENT_BASE_PATH}"/plugins/* /app/wp-content/plugins || true
41
- # cp -R "${WPCONTENT_BASE_PATH}"/themes/twentytwenty* /app/wp-content/themes || true
42
- # shopt -u dotglob nullglob
43
- # fi
32
+ if [ -n "${WPCONTENT_BASE_PATH:-}" ] && [ -d "${WPCONTENT_BASE_PATH}" ]; then
33
+ shopt -s dotglob nullglob
34
+ # Note: change this back to copy all, once using the WP Zip files
35
+ # cp -R "${WPCONTENT_BASE_PATH}"/* /app/wp-content
36
+ cp -R "${WPCONTENT_BASE_PATH}"/plugins/* /app/wp-content/plugins || true
37
+ cp -R "${WPCONTENT_BASE_PATH}"/themes/twentytwenty* /app/wp-content/themes || true
38
+ shopt -u dotglob nullglob
39
+ fi
44
40
 
45
41
  echo "⚙️ Installing WordPress core"
46
42
 
@@ -52,8 +48,8 @@ else
52
48
  --admin_email="$WP_ADMIN_EMAIL" \
53
49
  --locale="$WP_LOCALE"
54
50
 
55
- # echo "🔄 Setting permalinks"
56
- # wp rewrite structure '/%year%/%monthnum%/%day%/%postname%/'
51
+ echo "🔄 Setting permalinks"
52
+ wp rewrite structure '/%year%/%monthnum%/%day%/%postname%/'
57
53
  fi
58
54
 
59
55
  # Install plugins from WP_PLUGINS environment variable
@@ -1,24 +1,26 @@
1
- from shipit.providers.base import _exists
2
1
  import json
3
- import yaml
4
- from pathlib import Path
5
- from typing import Dict, Optional, Any, Set, List
2
+ import re
3
+ import shlex
6
4
  from enum import Enum
7
- from semantic_version import Version, NpmSpec
8
- from pydantic import Field
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Set
9
7
 
8
+ import yaml
9
+ from pydantic import Field
10
+ from pydantic_settings import SettingsConfigDict
11
+ from semantic_version import NpmSpec, Version
10
12
 
11
13
  from .base import (
14
+ Config,
12
15
  DetectResult,
13
16
  DependencySpec,
14
- Provider,
15
17
  MountSpec,
18
+ Provider,
16
19
  ServiceSpec,
17
20
  VolumeSpec,
18
- Config,
21
+ _exists,
19
22
  )
20
23
  from .staticfile import StaticFileProvider, StaticFileConfig
21
- from pydantic_settings import SettingsConfigDict
22
24
 
23
25
 
24
26
  class PackageManager(Enum):
@@ -106,6 +108,13 @@ class StaticGenerator(Enum):
106
108
  VITE = "vite"
107
109
  NEXT = "next"
108
110
  GATSBY = "gatsby"
111
+ ELEVENTY = "eleventy"
112
+ VITEPRESS = "vitepress"
113
+ VUEPRESS = "vuepress"
114
+ HEXO = "hexo"
115
+ METALSMITH = "metalsmith"
116
+ ASSEMBLE = "assemble"
117
+ HARP = "harp"
109
118
  DOCUSAURUS_OLD = "docusaurus-old"
110
119
  DOCUSAURUS = "docusaurus"
111
120
  SVELTE = "svelte"
@@ -119,6 +128,8 @@ class StaticGenerator(Enum):
119
128
  def get_output_dir(self) -> str:
120
129
  if self == StaticGenerator.NEXT:
121
130
  return "out"
131
+ elif self == StaticGenerator.ELEVENTY:
132
+ return "_site"
122
133
  elif self == StaticGenerator.NUXT_V3:
123
134
  return ".output/public"
124
135
  elif self in [
@@ -129,6 +140,12 @@ class StaticGenerator(Enum):
129
140
  return "dist"
130
141
  elif self == StaticGenerator.GATSBY:
131
142
  return "public"
143
+ elif self == StaticGenerator.HEXO:
144
+ return "public"
145
+ elif self == StaticGenerator.VITEPRESS:
146
+ return "docs/.vitepress/dist"
147
+ elif self == StaticGenerator.VUEPRESS:
148
+ return "docs/.vuepress/dist"
132
149
  elif self in [
133
150
  StaticGenerator.REMIX_OLD,
134
151
  StaticGenerator.REMIX,
@@ -137,38 +154,68 @@ class StaticGenerator(Enum):
137
154
  return "build/client"
138
155
  elif self == StaticGenerator.REMIX_V2_CLASSIC:
139
156
  return "public"
157
+ elif self == StaticGenerator.ASSEMBLE:
158
+ return "dist"
159
+ elif self == StaticGenerator.HARP:
160
+ return "www"
140
161
  elif self in [
141
162
  StaticGenerator.DOCUSAURUS,
142
163
  StaticGenerator.DOCUSAURUS_OLD,
143
164
  StaticGenerator.SVELTE,
165
+ StaticGenerator.METALSMITH,
144
166
  ]:
145
167
  return "build"
146
168
  else:
147
169
  return "dist"
148
170
 
149
171
  @classmethod
150
- def detect_generators_from_command(cls, build_command) -> List["StaticGenerator"]:
172
+ def detect_generators_from_command(
173
+ cls, build_command
174
+ ) -> List["StaticGenerator"]:
151
175
  commands = {
152
176
  "gatsby": [StaticGenerator.GATSBY],
153
177
  "astro": [StaticGenerator.ASTRO],
178
+ "@11ty/eleventy": [StaticGenerator.ELEVENTY],
179
+ "eleventy": [StaticGenerator.ELEVENTY],
154
180
  "remix-ssg": [StaticGenerator.REMIX_OLD],
155
181
  "remix": [StaticGenerator.REMIX_V2_CLASSIC, StaticGenerator.REMIX_V2],
156
182
  "vite": [StaticGenerator.VITE],
157
- "docusaurus": [StaticGenerator.DOCUSAURUS, StaticGenerator.DOCUSAURUS_OLD],
183
+ "vitepress": [StaticGenerator.VITEPRESS],
184
+ "vuepress": [StaticGenerator.VUEPRESS],
185
+ "hexo": [StaticGenerator.HEXO],
186
+ "metalsmith": [StaticGenerator.METALSMITH],
187
+ "harp": [StaticGenerator.HARP],
188
+ "docusaurus": [
189
+ StaticGenerator.DOCUSAURUS,
190
+ StaticGenerator.DOCUSAURUS_OLD,
191
+ ],
158
192
  "next": [StaticGenerator.NEXT],
159
193
  "nuxi": [StaticGenerator.NUXT_V3],
160
194
  "nuxt": [StaticGenerator.NUXT_OLD],
161
195
  "svelte-kit": [StaticGenerator.SVELTE],
162
196
  }
163
- build_command = build_command.split(" ")[0]
164
- if build_command in commands:
165
- return commands[build_command]
166
- else:
167
- return []
168
-
197
+ try:
198
+ tokens = shlex.split(build_command)
199
+ except ValueError:
200
+ tokens = build_command.split()
201
+
202
+ for index, token in enumerate(tokens):
203
+ if token == "grunt" and "assemble" in tokens[index + 1 :]:
204
+ return [StaticGenerator.ASSEMBLE]
205
+ if token in commands:
206
+ return commands[token]
207
+ return []
208
+
169
209
  def build_command(self) -> str:
170
210
  return {
171
211
  StaticGenerator.GATSBY: "gatsby build",
212
+ StaticGenerator.ELEVENTY: "@11ty/eleventy",
213
+ StaticGenerator.VITEPRESS: "vitepress build docs",
214
+ StaticGenerator.VUEPRESS: "vuepress build docs",
215
+ StaticGenerator.HEXO: "hexo generate",
216
+ StaticGenerator.METALSMITH: "metalsmith build",
217
+ StaticGenerator.ASSEMBLE: "grunt assemble",
218
+ StaticGenerator.HARP: "harp compile . www",
172
219
  StaticGenerator.ASTRO: "astro build",
173
220
  StaticGenerator.REMIX_OLD: "remix-ssg build",
174
221
  StaticGenerator.REMIX_V2: "vite build",
@@ -200,6 +247,7 @@ class NodeStaticConfig(StaticFileConfig):
200
247
 
201
248
  class NodeStaticProvider(StaticFileProvider):
202
249
  only_build: bool = False
250
+ _ASSEMBLE_DEST_PATTERN = re.compile(r"\bdest\s*:\s*['\"]([^'\"]+)['\"]")
203
251
 
204
252
  def __init__(
205
253
  self, path: Path, config: NodeStaticConfig, only_build: bool = False
@@ -227,7 +275,25 @@ class NodeStaticProvider(StaticFileProvider):
227
275
  package_json = cls.parse_package_json(path)
228
276
 
229
277
  if not config.static_generator:
230
- if cls.has_dependency(package_json, "gatsby"):
278
+ if cls.has_dependency(package_json, "@11ty/eleventy"):
279
+ config.static_generator = StaticGenerator.ELEVENTY
280
+ elif cls.has_dependency(package_json, "vitepress"):
281
+ config.static_generator = StaticGenerator.VITEPRESS
282
+ elif cls.has_dependency(package_json, "vuepress"):
283
+ config.static_generator = StaticGenerator.VUEPRESS
284
+ elif cls.has_dependency(package_json, "hexo") or cls.has_dependency(
285
+ package_json, "hexo-cli"
286
+ ):
287
+ config.static_generator = StaticGenerator.HEXO
288
+ elif cls.has_dependency(package_json, "metalsmith"):
289
+ config.static_generator = StaticGenerator.METALSMITH
290
+ elif cls.has_dependency(package_json, "assemble") or cls.has_dependency(
291
+ package_json, "grunt-assemble"
292
+ ):
293
+ config.static_generator = StaticGenerator.ASSEMBLE
294
+ elif cls.has_dependency(package_json, "harp"):
295
+ config.static_generator = StaticGenerator.HARP
296
+ elif cls.has_dependency(package_json, "gatsby"):
231
297
  config.static_generator = StaticGenerator.GATSBY
232
298
  elif cls.has_dependency(package_json, "astro"):
233
299
  config.static_generator = StaticGenerator.ASTRO
@@ -242,7 +308,17 @@ class NodeStaticProvider(StaticFileProvider):
242
308
  ) or cls.has_dependency(package_json, "@remix-run/dev", "0"):
243
309
  config.static_generator = StaticGenerator.REMIX_OLD
244
310
  elif cls.has_dependency(package_json, "@remix-run/dev"):
245
- has_vite = cls.has_dependency(package_json, "@remix-run/vite") or cls.has_dependency(package_json, "vite") or _exists(path, "vite.config.js", "vite.config.ts", "vite.config.mjs", "vite.config.cjs")
311
+ has_vite = (
312
+ cls.has_dependency(package_json, "@remix-run/vite")
313
+ or cls.has_dependency(package_json, "vite")
314
+ or _exists(
315
+ path,
316
+ "vite.config.js",
317
+ "vite.config.ts",
318
+ "vite.config.mjs",
319
+ "vite.config.cjs",
320
+ )
321
+ )
246
322
  if has_vite:
247
323
  config.static_generator = StaticGenerator.REMIX_V2
248
324
  else:
@@ -264,7 +340,12 @@ class NodeStaticProvider(StaticFileProvider):
264
340
  )
265
341
 
266
342
  if not config.static_dir:
267
- config.static_dir = config.static_generator.get_output_dir()
343
+ if config.static_generator:
344
+ config.static_dir = cls.get_static_dir(
345
+ path, package_json, config.static_generator
346
+ )
347
+ else:
348
+ config.static_dir = "dist"
268
349
 
269
350
  return config
270
351
 
@@ -282,6 +363,168 @@ class NodeStaticProvider(StaticFileProvider):
282
363
  except Exception:
283
364
  return None
284
365
 
366
+ @classmethod
367
+ def get_static_dir(
368
+ cls,
369
+ path: Path,
370
+ package_json: Optional[Dict[str, Any]],
371
+ static_generator: StaticGenerator,
372
+ ) -> str:
373
+ if static_generator == StaticGenerator.VITEPRESS:
374
+ root = cls._script_build_root(package_json, "vitepress")
375
+ root = root or cls._default_docs_root(path, ".vitepress")
376
+ return cls._rooted_output_dir(root, ".vitepress/dist")
377
+
378
+ if static_generator == StaticGenerator.VUEPRESS:
379
+ root = cls._script_build_root(package_json, "vuepress")
380
+ root = root or cls._default_docs_root(path, ".vuepress")
381
+ return cls._rooted_output_dir(root, ".vuepress/dist")
382
+
383
+ if static_generator == StaticGenerator.METALSMITH:
384
+ return cls._metalsmith_output_dir(path) or static_generator.get_output_dir()
385
+
386
+ if static_generator == StaticGenerator.ASSEMBLE:
387
+ return cls._assemble_output_dir(path) or static_generator.get_output_dir()
388
+
389
+ if static_generator == StaticGenerator.HARP:
390
+ return (
391
+ cls._harp_output_dir(package_json)
392
+ or static_generator.get_output_dir()
393
+ )
394
+
395
+ return static_generator.get_output_dir()
396
+
397
+ @classmethod
398
+ def _script_commands(
399
+ cls, package_json: Optional[Dict[str, Any]]
400
+ ) -> list[str]:
401
+ if not package_json:
402
+ return []
403
+ scripts = package_json.get("scripts", {})
404
+ if not isinstance(scripts, dict):
405
+ return []
406
+
407
+ preferred = ("generate", "build", "docs:build")
408
+ commands = [
409
+ scripts[name]
410
+ for name in preferred
411
+ if isinstance(scripts.get(name), str)
412
+ ]
413
+ commands.extend(
414
+ command
415
+ for name, command in scripts.items()
416
+ if name not in preferred and isinstance(command, str)
417
+ )
418
+ return commands
419
+
420
+ @classmethod
421
+ def _args_after_command(cls, command: str, executable: str) -> list[str]:
422
+ try:
423
+ tokens = shlex.split(command)
424
+ except ValueError:
425
+ tokens = command.split()
426
+
427
+ for index, token in enumerate(tokens):
428
+ if token == executable:
429
+ return tokens[index + 1 :]
430
+ return []
431
+
432
+ @classmethod
433
+ def _script_build_root(
434
+ cls, package_json: Optional[Dict[str, Any]], executable: str
435
+ ) -> Optional[str]:
436
+ for command in cls._script_commands(package_json):
437
+ args = cls._args_after_command(command, executable)
438
+ if not args or args[0] != "build":
439
+ continue
440
+ for arg in args[1:]:
441
+ if not arg.startswith("-"):
442
+ return cls._clean_output_dir(arg)
443
+ return None
444
+
445
+ @classmethod
446
+ def _default_docs_root(cls, path: Path, config_dir: str) -> str:
447
+ docs_path = path / "docs"
448
+ if (docs_path / config_dir).exists() or docs_path.exists():
449
+ return "docs"
450
+ return "."
451
+
452
+ @classmethod
453
+ def _rooted_output_dir(cls, root: str, output_dir: str) -> str:
454
+ root = cls._clean_output_dir(root)
455
+ if root == ".":
456
+ return output_dir
457
+ return f"{root}/{output_dir}"
458
+
459
+ @classmethod
460
+ def _clean_output_dir(cls, output_dir: str) -> str:
461
+ output_dir = output_dir.strip().rstrip("/")
462
+ if output_dir.startswith("./"):
463
+ output_dir = output_dir[2:]
464
+ return output_dir or "."
465
+
466
+ @classmethod
467
+ def _metalsmith_output_dir(cls, path: Path) -> Optional[str]:
468
+ for config_name in ("metalsmith.json", ".metalsmith.json"):
469
+ config_path = path / config_name
470
+ if not config_path.is_file():
471
+ continue
472
+ try:
473
+ config = json.loads(config_path.read_text())
474
+ except Exception:
475
+ continue
476
+ if not isinstance(config, dict):
477
+ continue
478
+ for key in ("destination", "dest"):
479
+ output_dir = config.get(key)
480
+ if isinstance(output_dir, str) and output_dir:
481
+ return cls._clean_output_dir(output_dir)
482
+ return None
483
+
484
+ @classmethod
485
+ def _assemble_output_dir(cls, path: Path) -> Optional[str]:
486
+ for config_name in ("Gruntfile.js", "Gruntfile.cjs"):
487
+ config_path = path / config_name
488
+ if not config_path.is_file():
489
+ continue
490
+ match = cls._ASSEMBLE_DEST_PATTERN.search(config_path.read_text())
491
+ if match:
492
+ return cls._clean_output_dir(match.group(1))
493
+ return None
494
+
495
+ @classmethod
496
+ def _harp_output_dir(
497
+ cls, package_json: Optional[Dict[str, Any]]
498
+ ) -> Optional[str]:
499
+ for command in cls._script_commands(package_json):
500
+ args = cls._args_after_command(command, "harp")
501
+ if not args:
502
+ continue
503
+ if args[0] == "compile":
504
+ args = args[1:]
505
+ positional_args = []
506
+ for index, arg in enumerate(args):
507
+ if arg in ("--output", "-o") and index + 1 < len(args):
508
+ return cls._clean_output_dir(args[index + 1])
509
+ if not arg.startswith("-"):
510
+ positional_args.append(arg)
511
+ if len(positional_args) >= 2:
512
+ return cls._clean_output_dir(positional_args[1])
513
+ return None
514
+
515
+ @classmethod
516
+ def _is_package_manager_build_command(cls, command: str) -> bool:
517
+ package_manager_prefixes = (
518
+ "npm run ",
519
+ "pnpm run ",
520
+ "pnpm ",
521
+ "yarn run ",
522
+ "yarn ",
523
+ "bun run ",
524
+ "bun ",
525
+ )
526
+ return command.startswith(package_manager_prefixes)
527
+
285
528
  @classmethod
286
529
  def has_dependency(
287
530
  cls,
@@ -314,26 +557,65 @@ class NodeStaticProvider(StaticFileProvider):
314
557
  ) -> Optional[DetectResult]:
315
558
  if config.commands.install:
316
559
  # Detect this provider from the install command
317
- if config.commands.install in ["npm install", "npm ci", "npm i", "pnpm install", "pnpm ci", "pnpm i", "yarn install", "yarn ci", "yarn i", "bun install", "bun ci", "bun i"]:
560
+ install_commands = [
561
+ "npm install",
562
+ "npm ci",
563
+ "npm i",
564
+ "pnpm install",
565
+ "pnpm ci",
566
+ "pnpm i",
567
+ "yarn install",
568
+ "yarn ci",
569
+ "yarn i",
570
+ "bun install",
571
+ "bun ci",
572
+ "bun i",
573
+ ]
574
+ if config.commands.install in install_commands:
318
575
  return DetectResult(cls.name(), 40)
319
576
 
577
+ has_package_manager_build_command = False
320
578
  if config.commands.build:
321
- if config.commands.build.startswith("npm run "):
322
- return DetectResult(cls.name(), 40)
323
- elif config.commands.build.startswith("pnpm run "):
324
- return DetectResult(cls.name(), 40)
325
- elif config.commands.build.startswith("yarn run "):
326
- return DetectResult(cls.name(), 40)
327
- elif config.commands.build.startswith("bun run "):
328
- return DetectResult(cls.name(), 40)
579
+ # Iterate over all generators and check if the build command matches
580
+ for static_generator in StaticGenerator:
581
+ if static_generator.build_command() in config.commands.build:
582
+ return DetectResult(cls.name(), 60)
329
583
 
330
- static_generators = StaticGenerator.detect_generators_from_command(config.commands.build)
584
+ static_generators = StaticGenerator.detect_generators_from_command(
585
+ config.commands.build
586
+ )
331
587
  if static_generators:
332
- return DetectResult(cls.name(), 40)
588
+ return DetectResult(cls.name(), 60)
589
+
590
+ has_package_manager_build_command = (
591
+ cls._is_package_manager_build_command(config.commands.build)
592
+ )
333
593
 
334
594
  package_json = cls.parse_package_json(path)
335
595
  if not package_json:
596
+ if has_package_manager_build_command:
597
+ return DetectResult(cls.name(), 40)
336
598
  return None
599
+ for build_command in cls._script_commands(package_json):
600
+ for static_generator in StaticGenerator:
601
+ if static_generator.build_command() in build_command:
602
+ return DetectResult(cls.name(), 60)
603
+ if StaticGenerator.detect_generators_from_command(build_command):
604
+ return DetectResult(cls.name(), 60)
605
+
606
+ pure_static_generators = [
607
+ "@11ty/eleventy",
608
+ "vitepress",
609
+ "vuepress",
610
+ "hexo",
611
+ "hexo-cli",
612
+ "metalsmith",
613
+ "assemble",
614
+ "grunt-assemble",
615
+ "harp",
616
+ "docusaurus",
617
+ "@docusaurus/core",
618
+ ]
337
619
  static_generators = [
338
620
  "astro",
339
621
  "vite",
@@ -341,12 +623,14 @@ class NodeStaticProvider(StaticFileProvider):
341
623
  "nuxt",
342
624
  "gatsby",
343
625
  "svelte",
344
- "docusaurus",
345
- "@docusaurus/core",
346
626
  "@remix-run/dev",
347
627
  ]
628
+ if any(cls.has_dependency(package_json, dep) for dep in pure_static_generators):
629
+ return DetectResult(cls.name(), 60)
348
630
  if any(cls.has_dependency(package_json, dep) for dep in static_generators):
349
631
  return DetectResult(cls.name(), 40)
632
+ if has_package_manager_build_command:
633
+ return DetectResult(cls.name(), 40)
350
634
  return None
351
635
 
352
636
  def dependencies(self) -> list[DependencySpec]:
@@ -367,16 +651,29 @@ class NodeStaticProvider(StaticFileProvider):
367
651
  cls,
368
652
  package_json: Optional[Dict[str, Any]],
369
653
  package_manager: PackageManager,
370
- static_generator: StaticGenerator,
654
+ static_generator: Optional[StaticGenerator],
371
655
  ) -> Optional[str]:
372
656
  if package_json:
373
657
  scripts = package_json.get("scripts", {})
658
+ if not isinstance(scripts, dict):
659
+ scripts = {}
660
+ docs_build_command = scripts.get("docs:build")
661
+ if (
662
+ static_generator
663
+ in [StaticGenerator.VITEPRESS, StaticGenerator.VUEPRESS]
664
+ and docs_build_command
665
+ ):
666
+ return package_manager.run_command("docs:build")
374
667
  generate_command = scripts.get("generate")
375
668
  if generate_command:
376
669
  return package_manager.run_command("generate")
377
670
  build_command = scripts.get("build")
378
671
  if build_command:
379
672
  return package_manager.run_command("build")
673
+ if docs_build_command:
674
+ return package_manager.run_command("docs:build")
675
+ if not static_generator:
676
+ return None
380
677
  command = static_generator.build_command()
381
678
  return package_manager.run_execute_command(command)
382
679
 
@@ -393,20 +690,22 @@ class NodeStaticProvider(StaticFileProvider):
393
690
  'env(CI="true", NODE_ENV="production", NPM_CONFIG_FUND="false")'
394
691
  if self.config.package_manager == PackageManager.NPM
395
692
  else None,
396
- # 'run("npx corepack enable", inputs=["package.json"], group="install")',
397
693
  f'run("{install_command}", inputs=["package.json"], group="install")',
398
694
  ],
399
695
  )
400
-
696
+
401
697
  def build_steps_build(self) -> list[str]:
402
698
  return filter(
403
699
  None,
404
700
  [
405
- f'run("{self.config.build_command}", outputs=[config.static_dir], group="build")'
406
- if not self.only_build
701
+ (
702
+ f'run("{self.config.build_command}", '
703
+ 'outputs=[config.static_dir], group="build")'
704
+ )
705
+ if self.config.build_command and not self.only_build
407
706
  else None,
408
707
  f'run("{self.config.build_command}", group="build")'
409
- if self.only_build
708
+ if self.config.build_command and self.only_build
410
709
  else None,
411
710
  ]
412
711
  )
@@ -77,10 +77,10 @@ class WordPressProvider(PhpProvider):
77
77
  'copy("wordpress/.htaccess", "{}/.htaccess".format(app.path), base="assets")'
78
78
  )
79
79
  return steps + super().build_steps_with_options(
80
- extra_ignore=[],
80
+ extra_ignore=["wp-content"],
81
81
  after_install=None,
82
82
  after_build=None
83
- ) # + ['copy("wp-content", "{}".format(wpcontent_base.path))']
83
+ ) + ['copy("wp-content", "{}".format(wpcontent_base.path))']
84
84
 
85
85
  def prepare_steps(self) -> Optional[list[str]]:
86
86
  return super().prepare_steps()
@@ -94,13 +94,13 @@ class WordPressProvider(PhpProvider):
94
94
  '-t {}".format(assets.serve_path, PORT, app.serve_path)'
95
95
  )
96
96
  return {
97
- # "wp": '"php {}/wp-cli.phar --allow-root --path={}".format(assets.serve_path, app.serve_path)',
97
+ "wp": '"php {}/wp-cli.phar --allow-root --path={}".format(assets.serve_path, app.serve_path)',
98
98
  "after_deploy": '"bash {}/setup-wp.sh".format(assets.serve_path)',
99
99
  **commands,
100
100
  }
101
101
 
102
102
  def mounts(self) -> list[MountSpec]:
103
- return super().mounts() # + [MountSpec("wpcontent_base")]
103
+ return super().mounts() + [MountSpec("wpcontent_base")]
104
104
 
105
105
  def volumes(self) -> list[VolumeSpec]:
106
106
  return [
@@ -114,7 +114,7 @@ class WordPressProvider(PhpProvider):
114
114
  def env(self) -> Optional[Dict[str, str]]:
115
115
  return {
116
116
  "PAGER": '"cat"',
117
- # "WPCONTENT_BASE_PATH": '"{}".format(wpcontent_base.serve_path)',
117
+ "WPCONTENT_BASE_PATH": '"{}".format(wpcontent_base.serve_path)',
118
118
  **(super().env() or {}),
119
119
  }
120
120
 
@@ -149,29 +149,29 @@ class WasmerRunner:
149
149
  },
150
150
  "phpix": {
151
151
  "dependencies": {
152
- "latest": "wasmer/phpix-32@=0.1.13803",
153
- "8.3": "wasmer/phpix-32@=0.1.13803",
154
- "8.3.29": "wasmer/phpix-32@=0.1.13803",
155
- "8.2": "wasmer/phpix-32@=0.1.13803",
156
- "8.1": "wasmer/phpix-32@=0.1.13803",
157
- "7.4": "wasmer/phpix-32@=0.1.13803",
152
+ "latest": "phpix/phpix-83-32bit@=0.2.0-rc.1",
153
+ "8.3": "phpix/phpix-83-32bit@=0.2.0-rc.1",
154
+ "8.3.29": "phpix/phpix-83-32bit@=0.2.0-rc.1",
155
+ "8.2": "phpix/phpix-82-32bit@=0.2.0-rc.1",
156
+ "8.1": "phpix/phpix-81-32bit@=0.2.0-rc.1",
157
+ "7.4": "php/php-32@=7.4.3301", # Note, we don't have PHPix + PHP 7.4 yet
158
158
  },
159
159
  "architecture_dependencies": {
160
160
  "64-bit": {
161
- "latest": "wasmer/phpix-64@=0.1.13803",
162
- "8.3": "wasmer/phpix-64@=0.1.13803",
163
- "8.3.29": "wasmer/phpix-64@=0.1.13803",
164
- "8.2": "wasmer/phpix-64@=0.1.13803",
165
- "8.1": "wasmer/phpix-64@=0.1.13803",
166
- "7.4": "wasmer/phpix-64@=0.1.13803",
161
+ "latest": "phpix/phpix-83-64bit@=0.2.0-rc.1",
162
+ "8.3": "phpix/phpix-83-64bit@=0.2.0-rc.1",
163
+ "8.3.29": "phpix/phpix-83-64bit@=0.2.0-rc.1",
164
+ "8.2": "phpix/phpix-82-64bit@=0.2.0-rc.1",
165
+ "8.1": "phpix/phpix-81-64bit@=0.2.0-rc.1",
166
+ "7.4": "php/php-64@=7.4.3301",
167
167
  },
168
168
  "32-bit": {
169
- "latest": "wasmer/phpix-32@=0.1.13803",
170
- "8.3": "wasmer/phpix-32@=0.1.13803",
171
- "8.3.29": "wasmer/phpix-32@=0.1.13803",
172
- "8.2": "wasmer/phpix-32@=0.1.13803",
173
- "8.1": "wasmer/phpix-32@=0.1.13803",
174
- "7.4": "wasmer/phpix-32@=0.1.13803",
169
+ "latest": "phpix/phpix-83-32bit@=0.2.0-rc.1",
170
+ "8.3": "phpix/phpix-83-32bit@=0.2.0-rc.1",
171
+ "8.3.29": "phpix/phpix-83-32bit@=0.2.0-rc.1",
172
+ "8.2": "phpix/phpix-82-32bit@=0.2.0-rc.1",
173
+ "8.1": "phpix/phpix-81-32bit@=0.2.0-rc.1",
174
+ "7.4": "php/php-32@=7.4.3301",
175
175
  },
176
176
  },
177
177
  "scripts": {"php", "phpix"},
@@ -231,6 +231,8 @@ class WasmerRunner:
231
231
  )
232
232
  provider_config.cross_platform = "wasix_wasm32"
233
233
  provider_config.precompile_python = True
234
+ elif isinstance(provider_config, PhpConfig):
235
+ provider_config.phpix = True
234
236
  self.provider_config = provider_config
235
237
  return provider_config
236
238
 
@@ -0,0 +1,5 @@
1
+ __all__ = ["version", "version_info"]
2
+
3
+
4
+ version = "0.19.7"
5
+ version_info = (0, 19, 7, "final", 0)
@@ -237,6 +237,55 @@ class E2ECase:
237
237
  serve_pattern=r"server is listening on",
238
238
  http=[HTTPRequest(path="/", body_match=r"Welcome to MkDocs with Plugins")],
239
239
  ),
240
+ # Eleventy / 11ty static site
241
+ E2ECase(
242
+ path="examples/eleventy",
243
+ serve_pattern=r"server is listening on",
244
+ http=[HTTPRequest(path="/", body_match=r"Eleventy Example")],
245
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
246
+ ),
247
+ # VitePress static documentation site
248
+ E2ECase(
249
+ path="examples/vitepress",
250
+ serve_pattern=r"server is listening on",
251
+ http=[HTTPRequest(path="/", body_match=r"VitePress Example")],
252
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
253
+ ),
254
+ # VuePress static documentation site
255
+ E2ECase(
256
+ path="examples/vuepress",
257
+ serve_pattern=r"server is listening on",
258
+ http=[HTTPRequest(path="/", body_match=r"VuePress Example")],
259
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
260
+ ),
261
+ # Hexo static blog
262
+ E2ECase(
263
+ path="examples/hexo",
264
+ serve_pattern=r"server is listening on",
265
+ http=[HTTPRequest(path="/", body_match=r"Hexo Example")],
266
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
267
+ ),
268
+ # Metalsmith static site
269
+ E2ECase(
270
+ path="examples/metalsmith",
271
+ serve_pattern=r"server is listening on",
272
+ http=[HTTPRequest(path="/", body_match=r"Metalsmith Example")],
273
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
274
+ ),
275
+ # Assemble static site
276
+ E2ECase(
277
+ path="examples/assemble",
278
+ serve_pattern=r"server is listening on",
279
+ http=[HTTPRequest(path="/", body_match=r"Assemble Example")],
280
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
281
+ ),
282
+ # Harp static site
283
+ E2ECase(
284
+ path="examples/harp",
285
+ serve_pattern=r"server is listening on",
286
+ http=[HTTPRequest(path="/", body_match=r"Harp Example")],
287
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
288
+ ),
240
289
  # Python FastAPI app on Uvicorn
241
290
  E2ECase(
242
291
  path="examples/python-fastapi",
@@ -1,5 +0,0 @@
1
- __all__ = ["version", "version_info"]
2
-
3
-
4
- version = "0.19.6.dev1"
5
- version_info = (0, 19, 6, "dev", 1)
File without changes
File without changes