Feat: Creating vite-plugin.js for boboko/core
This commit is contained in:
@@ -31,6 +31,56 @@ This is shorthand for `docker compose -f docker-compose.dev.yml -f docker-compos
|
||||
|
||||
Skipping this step is the most common cause of "my change isn't showing up."
|
||||
|
||||
## JS/CSS: no separate npm package
|
||||
|
||||
This package's JS (Stimulus controllers) and CSS ship as plain source files under `resources/js/` and `resources/css/`, read directly by a consumer app's own Vite build — there is no separate `@boboko/core` npm package, and no `npm install`/`file:` dependency step of any kind.
|
||||
|
||||
The reason: Composer already gives every environment one single, unconditional path — `vendor/boboko/core` — whether that resolves to a real symlink into `../boboko-core` (local path repo) or a real installed copy (tagged VCS release). A consumer's `vite.config.js` and JS entry point just read straight from that path, so there is nothing to toggle on the JS side — whatever Composer resolved is exactly what Vite sees, automatically, in both dev and prod.
|
||||
|
||||
**Stable entry point.** A consumer imports from [resources/js/index.js](resources/js/index.js) only — never from a path reaching into a specific module's internals (e.g. `resources/js/checkout/index.js` directly). That barrel file re-exports whatever a consumer needs (currently just `registerCheckout`), so this package's internal file layout can change without breaking every consumer's own entry point:
|
||||
|
||||
```js
|
||||
// consumer app's resources/js/app.js
|
||||
import { registerCheckout } from "../../vendor/boboko/core/resources/js/index.js";
|
||||
registerCheckout(application);
|
||||
```
|
||||
|
||||
```php
|
||||
{{-- consumer app's layout --}}
|
||||
@vite(['vendor/boboko/core/resources/css/checkout.css', 'resources/css/app.css', 'resources/js/app.js'])
|
||||
```
|
||||
|
||||
**What a consumer's `vite.config.js` needs**, because `vendor/boboko/core` is a symlink in local path-repo dev (not a real directory):
|
||||
|
||||
```js
|
||||
export default defineConfig({
|
||||
server: {
|
||||
watch: {
|
||||
// vendor/boboko/core is a symlink into ../boboko-core in local
|
||||
// path-repo dev. Vite/chokidar don't follow symlinks for watched
|
||||
// files by default, so edits to core's source wouldn't otherwise
|
||||
// trigger HMR. No-op against a real installed copy (tagged VCS
|
||||
// release) in production — there's no symlink to follow, and
|
||||
// production only ever runs a one-shot `npm run build`, which
|
||||
// doesn't watch anything regardless.
|
||||
followSymlinks: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Bare imports inside this package's own JS (`leaflet`, `@hotwired/stimulus`) resolve against the *consumer's* `node_modules` — Node's normal upward `node_modules` resolution walks from `vendor/boboko/core/resources/js/...` up through `vendor/boboko/`, `vendor/`, to the consumer app's root, where `node_modules` lives. This works with zero extra config as long as `vendor/boboko/core` sits inside the consumer's own directory tree (true for both the symlink and the real-copy case) — a consuming app's `vite`-equivalent Docker service just needs the same bind mount PHP containers already get, landing at the same path:
|
||||
|
||||
```yaml
|
||||
# consumer app's docker-compose.core-dev.yml
|
||||
services:
|
||||
vite:
|
||||
volumes:
|
||||
- ../boboko-core:/app/vendor/boboko/core
|
||||
```
|
||||
|
||||
(Match whatever the consumer's Vite container's working directory actually is — `/app` above, `/var/www/html` for the PHP containers in `boboko-test`'s convention.)
|
||||
|
||||
## Verifying changes against a real database
|
||||
|
||||
There is no automated test suite for this package — too much of Lunar's behavior (table prefixing, nested sets, translatable attributes, Filament panel filters) only breaks in combination, against real Postgres, in a way that's impractical to fake in isolation. Instead, verify changes directly against a consumer app's live database. The practical workflow used throughout this package's `MigrateImport` feature:
|
||||
|
||||
@@ -122,6 +122,63 @@ Docker Compose merges `volumes:` lists additively across `-f` files, so the over
|
||||
|
||||
---
|
||||
|
||||
## Frontend Assets (JS/CSS)
|
||||
|
||||
A module's JS (Stimulus controllers) and CSS ship as plain source files under `resources/js/` and `resources/css/` — **there is no separate npm package per module.** A module is never `npm install`ed; its frontend assets are read directly by the consuming app's own Vite build, straight out of `vendor/boboko/<module>`.
|
||||
|
||||
This mirrors the PHP story above exactly: Composer already gives every environment one single, unconditional path — `vendor/boboko/<module>` — whether that resolves to a symlink into a sibling checkout (local path repo) or a real installed copy (tagged VCS release). A consumer's `vite.config.js` and JS entry point read from that same path, so there is nothing to toggle on the JS side — whatever Composer resolved is exactly what Vite sees, in both dev and prod, automatically.
|
||||
|
||||
**Each module exposes one stable JS entry point** — `resources/js/index.js` — that re-exports whatever a consumer needs, e.g. `boboko-core`'s:
|
||||
|
||||
```js
|
||||
// boboko-core/resources/js/index.js
|
||||
export { registerCheckout } from './checkout/index.js'
|
||||
```
|
||||
|
||||
A consuming app imports from that one file only, never from a path reaching into a module's internal folder structure directly:
|
||||
|
||||
```js
|
||||
// consumer app's resources/js/app.js
|
||||
import { registerCheckout } from "../../vendor/boboko/core/resources/js/index.js";
|
||||
registerCheckout(application);
|
||||
```
|
||||
|
||||
```php
|
||||
{{-- consumer app's layout --}}
|
||||
@vite(['vendor/boboko/core/resources/css/checkout.css', 'resources/css/app.css', 'resources/js/app.js'])
|
||||
```
|
||||
|
||||
This keeps a module's internal file layout free to change without breaking every consumer's entry point — the same reasoning as PSR-4 namespaces for PHP, just for JS imports.
|
||||
|
||||
**A consuming app's `vite.config.js` needs one addition**, because `vendor/boboko/<module>` is a symlink in local path-repo dev (not a real directory Vite would otherwise watch through):
|
||||
|
||||
```js
|
||||
export default defineConfig({
|
||||
server: {
|
||||
watch: {
|
||||
// vendor/boboko/<module> is a symlink into ../boboko-<module> in
|
||||
// local path-repo dev. Vite/chokidar don't follow symlinks for
|
||||
// watched files by default, so edits to a module's source
|
||||
// wouldn't otherwise trigger HMR. No-op against a real installed
|
||||
// copy (tagged VCS release) in production.
|
||||
followSymlinks: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Bare imports inside a module's own JS (e.g. `leaflet`, `@hotwired/stimulus`) resolve against the **consumer's** `node_modules` via Node's normal upward resolution walk from `vendor/boboko/<module>/resources/js/...` — no extra config needed, as long as `vendor/boboko/<module>` sits inside the consumer's own directory tree (true for both the symlink and real-copy case). The consumer's Vite Docker service (if any) needs the same bind mount the PHP containers already get, landing at the equivalent path relative to its own working directory:
|
||||
|
||||
```yaml
|
||||
# consumer app's docker-compose.core-dev.yml
|
||||
services:
|
||||
vite:
|
||||
volumes:
|
||||
- ../boboko-core:/app/vendor/boboko/core # match /app to the vite service's actual workdir
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating a New Module
|
||||
|
||||
**1. Create the repository and `composer.json`:**
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@boboko/core",
|
||||
"version": "0.23.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Portable Stimulus controllers and styles for boboko-core's cart + checkout module. Installed as a real npm dependency (file:../boboko-core in dev, a tagged git install in prod) so a consuming app's `npm install` resolves this package's own dependencies (leaflet, @hotwired/stimulus) transitively, the same way `composer update boboko/*` does for PHP. See CONTRIBUTE.md's \"JS/CSS: a real npm package\" section.",
|
||||
"exports": {
|
||||
".": "./resources/js/index.js",
|
||||
"./checkout": "./resources/js/checkout/index.js",
|
||||
"./checkout/*": "./resources/js/checkout/*",
|
||||
"./css/*": "./resources/css/*",
|
||||
"./vite-plugin": "./vite-plugin.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hotwired/stimulus": "^3.2.2",
|
||||
"leaflet": "^1.9.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^8.0.0"
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import BbkPaymentController from './bbk-payment-controller'
|
||||
// When this module moves to boboko-core this file ships with it unchanged;
|
||||
// only that one import line in the host entry point differs per project.
|
||||
export function registerCheckout(application) {
|
||||
console.log('[@boboko/core] checkout module loaded from', import.meta.url, '- test 2')
|
||||
application.register('bbk-add-to-cart', BbkAddToCartController)
|
||||
application.register('bbk-box-now-locker', BbkBoxNowLockerController)
|
||||
application.register('bbk-cart', BbkCartController)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Single stable JS entry point for this package. A consuming app imports
|
||||
// from here (vendor/boboko/core/resources/js/index.js), never from a path
|
||||
// reaching into a specific module's internals — so this file's exports can
|
||||
// grow or its modules' internal layout can change without breaking every
|
||||
// consumer's own entry point.
|
||||
//
|
||||
// stoic_embed.js is not re-exported here: per its own docblock, it's a
|
||||
// standalone vendored script meant to be included directly, not imported.
|
||||
export { registerCheckout } from './checkout/index.js'
|
||||
@@ -18,13 +18,16 @@ use Modules\Core\Cart\Services\CartService;
|
||||
* CheckoutTranslationsSeeder rather than shipped as lang/ files.
|
||||
*
|
||||
* A consuming app wires this module in with:
|
||||
* 1. `php artisan vendor:publish --tag=core-checkout-assets` — copies
|
||||
* resources/js/checkout/** and resources/css/checkout.css into the
|
||||
* host's own resources/ tree. Vite only ever bundles from a host's
|
||||
* own resources/ directory, so these are published (an explicit,
|
||||
* host-owned, re-publishable copy) rather than imported cross-package.
|
||||
* 2. `import { registerCheckout } from './checkout'` in the host's own
|
||||
* JS entry point, and a @vite entry for the published checkout.css.
|
||||
* 1. `"@boboko/core": "file:../boboko-core"` as an npm dependency (see this
|
||||
* package's own package.json `exports`), with a bind-mount of the core
|
||||
* checkout into the host's Vite container so the `file:` symlink
|
||||
* resolves in dev (see 3dealer's docker-compose.core-dev.yml) and
|
||||
* `resolve.preserveSymlinks: true` in the host's vite.config.js so bare
|
||||
* imports (stimulus, leaflet) still resolve against the host's own
|
||||
* node_modules through that symlink.
|
||||
* 2. `import { registerCheckout } from '@boboko/core/checkout'` in the
|
||||
* host's own JS entry point, and a @vite entry for
|
||||
* `node_modules/@boboko/core/resources/css/checkout.css`.
|
||||
* 3. `@include('checkout::drawer')` in the host's own layout.
|
||||
* See config/checkout.php for the handful of per-site settings (login
|
||||
* route, single-country mode, ...) a host is expected to publish and
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Vite integration for @boboko/core, mirroring how CoreServiceProvider owns
|
||||
// and ships its own PHP wiring instead of making every consumer hand-copy
|
||||
// it. A consuming app's vite.config.js just does:
|
||||
//
|
||||
// import { boboko } from '@boboko/core/vite-plugin'
|
||||
// export default defineConfig({ plugins: [..., boboko()] })
|
||||
//
|
||||
// All of the settings below exist only because @boboko/core is typically
|
||||
// installed as a local `file:../boboko-core` path dependency in dev
|
||||
// (symlinked into node_modules by npm) rather than a real installed copy —
|
||||
// see this package's own CONTRIBUTE.md.
|
||||
export function boboko() {
|
||||
return {
|
||||
name: 'boboko-core',
|
||||
config() {
|
||||
return {
|
||||
optimizeDeps: {
|
||||
// @boboko/core is a live local dependency in dev, not a
|
||||
// stable third-party lib. Vite's dependency pre-bundler
|
||||
// otherwise caches it once under node_modules/.vite/deps
|
||||
// and never re-scans it on a plain source edit, silently
|
||||
// serving a stale bundle. Excluding it makes Vite treat
|
||||
// it like first-party source: always transformed live.
|
||||
exclude: ['@boboko/core'],
|
||||
// Excluding @boboko/core above means its own dependencies
|
||||
// (leaflet, @hotwired/stimulus) are no longer discovered
|
||||
// by Vite's dependency scanner, since that scanner only
|
||||
// crawls from already-optimized entry points. Without
|
||||
// this, leaflet is served straight from its raw UMD
|
||||
// source instead of the pre-bundled ESM shim, and
|
||||
// `import L from 'leaflet'` fails with "does not provide
|
||||
// an export named 'default'". Forces pre-bundling
|
||||
// regardless of how they're reached in the import graph.
|
||||
include: ['leaflet', '@hotwired/stimulus'],
|
||||
},
|
||||
resolve: {
|
||||
// The local `file:../boboko-core` form installs as a
|
||||
// symlink, same as npm always does for a local `file:`
|
||||
// target. Without this, Vite resolves the symlink's bare
|
||||
// imports relative to its real path outside the
|
||||
// consumer's own root, where there's no node_modules,
|
||||
// instead of from the symlink's location in the
|
||||
// consumer's own node_modules. Harmless no-op against a
|
||||
// real installed copy (tagged VCS release).
|
||||
preserveSymlinks: true,
|
||||
},
|
||||
server: {
|
||||
watch: {
|
||||
// Same symlink as above: Vite/chokidar don't follow
|
||||
// symlinks for watched files by default, so edits to
|
||||
// core's source wouldn't otherwise trigger HMR.
|
||||
// No-op against a real installed copy.
|
||||
followSymlinks: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user