ultimate-jekyll-manager 1.7.2 → 1.8.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.
- package/.claude/scheduled_tasks.lock +1 -0
- package/CHANGELOG.md +61 -1
- package/CLAUDE.md +36 -15
- package/README.md +4 -2
- package/TODO-AUTH-TESTING.md +1 -1
- package/dist/assets/themes/newsflash/README.md +58 -0
- package/dist/assets/themes/newsflash/_config.scss +138 -0
- package/dist/assets/themes/newsflash/_theme.js +27 -0
- package/dist/assets/themes/newsflash/_theme.scss +37 -0
- package/dist/assets/themes/newsflash/css/base/_mixins.scss +50 -0
- package/dist/assets/themes/newsflash/css/base/_root.scss +134 -0
- package/dist/assets/themes/newsflash/css/base/_typography.scss +49 -0
- package/dist/assets/themes/newsflash/css/base/_utilities.scss +58 -0
- package/dist/assets/themes/newsflash/css/components/_badges.scss +65 -0
- package/dist/assets/themes/newsflash/css/components/_buttons.scss +139 -0
- package/dist/assets/themes/newsflash/css/components/_cards.scss +52 -0
- package/dist/assets/themes/newsflash/css/components/_editorial.scss +182 -0
- package/dist/assets/themes/newsflash/css/components/_forms.scss +75 -0
- package/dist/assets/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
- package/dist/assets/themes/newsflash/css/components/_panels.scss +91 -0
- package/dist/assets/themes/newsflash/css/components/_ticker.scss +70 -0
- package/dist/assets/themes/newsflash/css/layout/_general.scss +264 -0
- package/dist/assets/themes/newsflash/css/layout/_navigation.scss +164 -0
- package/dist/assets/themes/newsflash/js/initialize-tooltips.js +20 -0
- package/dist/assets/themes/newsflash/js/masthead-scroll.js +29 -0
- package/dist/assets/themes/newsflash/pages/404/index.scss +27 -0
- package/dist/assets/themes/newsflash/pages/about/index.scss +70 -0
- package/dist/assets/themes/newsflash/pages/blog/index.scss +17 -0
- package/dist/assets/themes/newsflash/pages/blog/post.js +29 -0
- package/dist/assets/themes/newsflash/pages/blog/post.scss +164 -0
- package/dist/assets/themes/newsflash/pages/index.scss +159 -0
- package/dist/assets/themes/newsflash/pages/pricing/index.scss +194 -0
- package/dist/assets/themes/newsflash/pages/test/libraries/layers/index.js +9 -0
- package/dist/assets/themes/newsflash/pages/test/libraries/layers/index.scss +7 -0
- package/dist/commands/blogify.js +6 -3
- package/dist/commands/test.js +34 -5
- package/dist/defaults/CLAUDE.md +17 -4
- package/dist/defaults/dist/_includes/core/pricing/resolve-plan.html +59 -0
- package/dist/defaults/dist/_includes/themes/classy/frontend/sections/footer.html +20 -3
- package/dist/defaults/dist/_layouts/themes/classy/admin/core/minimal-viewport-locked.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/admin/core/minimal.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +5 -40
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/pricing.html +33 -34
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/core/base.html +61 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/404.html +86 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/about.html +353 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/categories/category.html +105 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/categories/index.html +93 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/index.html +373 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/post.html +289 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/tags/index.html +90 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/tags/tag.html +107 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/contact.html +340 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/index.html +522 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/pricing.html +485 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/team/index.html +207 -0
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/team/member.html +134 -0
- package/dist/defaults/test/README.md +4 -0
- package/dist/gulp/tasks/jekyll.js +4 -2
- package/dist/test/runner.js +50 -3
- package/dist/test/suites/build/attach-log-file.test.js +102 -0
- package/dist/test/suites/build/theme-contract.test.js +173 -0
- package/dist/test/utils/extended-mode-warning.js +13 -0
- package/dist/utils/attach-log-file.js +70 -43
- package/docs/appearance.md +1 -0
- package/docs/assets.md +9 -0
- package/docs/audit.md +27 -7
- package/docs/build-system.md +57 -0
- package/docs/common-mistakes.md +15 -0
- package/docs/{project-structure.md → directory-structure.md} +1 -1
- package/docs/environment-detection.md +1 -1
- package/docs/javascript-libraries.md +38 -1
- package/docs/layouts-and-pages.md +146 -0
- package/docs/local-development.md +1 -8
- package/docs/logging.md +30 -0
- package/docs/migration.md +131 -0
- package/docs/no-inline-scripts.md +304 -0
- package/docs/purgecss.md +164 -0
- package/docs/seo.md +131 -4
- package/docs/templating.md +23 -0
- package/docs/test-boot-layer.md +1 -1
- package/docs/test-framework.md +56 -8
- package/docs/themes.md +254 -13
- package/logs/test.log +111 -0
- package/package.json +1 -1
|
@@ -29,3 +29,149 @@ asset_path: categories/category
|
|
|
29
29
|
**Example:**
|
|
30
30
|
- One-off page: `pages/categories.html` → `src/assets/css/pages/categories/index.scss`
|
|
31
31
|
- Repeating layout: `_layouts/category.html` → `src/assets/css/pages/categories/category.scss` (set `asset_path: categories/category` in layout frontmatter)
|
|
32
|
+
|
|
33
|
+
## Customizing Default Pages (blueprints)
|
|
34
|
+
|
|
35
|
+
Default pages live in `src/defaults/dist/_layouts/themes/[theme-id]/frontend/pages/`. Consumers customize them through frontmatter only — see [assets.md → Customizing Default Pages via Frontmatter](assets.md#customizing-default-pages-via-frontmatter) for the mechanism (`page.resolved`, reading the default layout first).
|
|
36
|
+
|
|
37
|
+
For each relevant default page:
|
|
38
|
+
|
|
39
|
+
1. Create a page in the consuming project's `src/pages/` directory
|
|
40
|
+
2. Use ONLY frontmatter to customize — NO HTML content needed
|
|
41
|
+
3. Set `layout: blueprint/[page-name]` (e.g., `layout: blueprint/pricing`)
|
|
42
|
+
4. Customize the frontmatter values to match the brand and purpose
|
|
43
|
+
|
|
44
|
+
### File Extension Rules
|
|
45
|
+
|
|
46
|
+
- `.md` — Use for frontmatter-only customization (no HTML content)
|
|
47
|
+
- `.html` — Use only when adding custom HTML content beyond the layout
|
|
48
|
+
|
|
49
|
+
### Frontmatter Rules
|
|
50
|
+
|
|
51
|
+
- Default pages (blueprint layouts): Do NOT include `meta.title` or `meta.description` — already set in the layout
|
|
52
|
+
- Do NOT include `theme` config (e.g., `theme.main.class`) unless explicitly changing from defaults
|
|
53
|
+
- `superheadline`: Homepage keeps default icon AND text; other pages may customize `text` but keep the default `icon`
|
|
54
|
+
|
|
55
|
+
### Page Exclusions
|
|
56
|
+
|
|
57
|
+
Do NOT modify these pages:
|
|
58
|
+
|
|
59
|
+
- `auth/*` — Authentication pages (signin, signup, reset, oauth2)
|
|
60
|
+
- `payment/*` — Payment pages (checkout, confirmation)
|
|
61
|
+
- `account/*` — Account management pages
|
|
62
|
+
- `app.html` — App page
|
|
63
|
+
- `404.html` — Error page
|
|
64
|
+
|
|
65
|
+
## Default Pages — Customization Levels
|
|
66
|
+
|
|
67
|
+
| Page | File | Layout | Level | Customize | Keep defaults |
|
|
68
|
+
|------|------|--------|-------|-----------|---------------|
|
|
69
|
+
| Homepage | `src/pages/index.md` | `blueprint/index` | Full | `hero`, `features`, `testimonials`, `stats`, `cta` | `superheadline` (icon AND text) |
|
|
70
|
+
| Pricing | `src/pages/pricing.md` | `blueprint/pricing` | Full | plan `features`/`pricing`/`definitions`/`price_per_unit`, `testimonials`, `faqs` | plan `id`/`name`/`tagline`, plan order |
|
|
71
|
+
| About | `src/pages/about.md` | `blueprint/about` | Full | `hero`, `mission`, `vision`, `story`, `values`, `team` | — |
|
|
72
|
+
| Contact | `src/pages/contact.md` | `blueprint/contact` | Minimal | `testimonials`, `faqs` ONLY | `hero`, `contact_methods`, `contact_form`, `stats` |
|
|
73
|
+
| Download | `src/pages/download.md` | `blueprint/download` | Minimal | `testimonials`, `faqs` ONLY | `hero`, `platforms`, `features`, `system_requirements` |
|
|
74
|
+
|
|
75
|
+
### Homepage hero specifics
|
|
76
|
+
|
|
77
|
+
Always use the tagline format `tagline: "Introducing {{ site.brand.name }}"`. The hero supports optional display modes via `hero.display` — use only if the site benefits from an in-hero demo, CTA, or video:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
hero:
|
|
81
|
+
display:
|
|
82
|
+
type: input # or: form, video, custom
|
|
83
|
+
view: side # or: bottom (default)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- `bottom` (default) — content stacked vertically, display element below text; `side` — side-by-side, display element on the right
|
|
87
|
+
- Types: `input` (single field + button, e.g. email signup), `form` (multiple fields), `video` (embedded player), `custom` (only for something the others can't achieve)
|
|
88
|
+
- Reference examples: `src/defaults/dist/pages/test/components/hero-demo-{input,form,video,side,custom}.html`
|
|
89
|
+
|
|
90
|
+
### Pricing plan features
|
|
91
|
+
|
|
92
|
+
Use YAML comments to separate feature types in each plan's `features` array:
|
|
93
|
+
|
|
94
|
+
- `# Common features` — listed in EVERY plan, displayed aligned across all plan cards
|
|
95
|
+
- `# Additional features` — features introduced in THIS plan only, appear in the "Everything in X, plus:" section
|
|
96
|
+
|
|
97
|
+
Do NOT repeat additional features that are unchanged from the previous plan (they're inherited automatically).
|
|
98
|
+
|
|
99
|
+
### Testimonials (any page)
|
|
100
|
+
|
|
101
|
+
Each testimonial requires `quote` (1-2 sentences max), `author`, `role`, `company`, `initial` (first letter of name, shown in avatar). Use 3 for balanced display, keep quotes relevant to the page (pricing → value/ROI, contact → support experience), vary roles to show a broad customer base.
|
|
102
|
+
|
|
103
|
+
### FAQs (any page)
|
|
104
|
+
|
|
105
|
+
Each FAQ requires `question` + `answer` (answers can include HTML like `<br><br>`). Use 3-5 per page, focused on the page's topic (pricing → billing/trials/refunds, contact → response times/channels), ordered by most commonly asked. `{{ site.brand.name }}` works in answers.
|
|
106
|
+
|
|
107
|
+
## Dashboard Resource Pages (list/detail/edit)
|
|
108
|
+
|
|
109
|
+
For dashboard resource pages (e.g., forms, users, orders), use separate pages — do NOT use a single page with show/hide toggling.
|
|
110
|
+
|
|
111
|
+
**File structure:**
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
src/pages/dashboard/items/
|
|
115
|
+
index.html ← list page
|
|
116
|
+
view.html ← detail page
|
|
117
|
+
edit.html ← edit page (optional)
|
|
118
|
+
|
|
119
|
+
src/assets/js/pages/dashboard/items/
|
|
120
|
+
index.js
|
|
121
|
+
view.js
|
|
122
|
+
edit.js ← (optional)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**URL pattern:** `/dashboard/items` (list) · `/dashboard/items/view?id={id}` (detail) · `/dashboard/items/edit?id={id}` (edit, optional).
|
|
126
|
+
|
|
127
|
+
**Frontmatter:** the view page's breadcrumbs follow Home → Dashboard → Items (linked) → Details (active, no href):
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
meta:
|
|
131
|
+
breadcrumbs:
|
|
132
|
+
- label: Home
|
|
133
|
+
href: /
|
|
134
|
+
- label: Dashboard
|
|
135
|
+
href: /dashboard
|
|
136
|
+
- label: Items
|
|
137
|
+
href: /dashboard/items
|
|
138
|
+
- label: Details
|
|
139
|
+
active: true
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**JS conventions:**
|
|
143
|
+
|
|
144
|
+
- `view.js` must redirect to the list page if no `?id=` param is present:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
const id = new URLSearchParams(window.location.search).get('id');
|
|
148
|
+
if (!id) {
|
|
149
|
+
window.location.href = '/dashboard/items';
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
- Links from the list page to detail use `/dashboard/items/view?id=${item.id}`
|
|
155
|
+
- After creating an item, redirect to `/dashboard/items/view?id=${item.id}`
|
|
156
|
+
- Delete on the view page redirects back to the list page
|
|
157
|
+
|
|
158
|
+
## Creating Custom Pages (beyond blueprints)
|
|
159
|
+
|
|
160
|
+
When a page needs custom HTML (no blueprint fits):
|
|
161
|
+
|
|
162
|
+
1. Place it in `src/pages/` with `.html` extension
|
|
163
|
+
2. Use `layout: themes/[ site.theme.id ]/frontend/core/base`
|
|
164
|
+
3. **Include `meta.title` and `meta.description`** in frontmatter (see [seo.md](seo.md))
|
|
165
|
+
4. Follow the HTML patterns from default pages: `{% uj_icon %}` for icons, theme-adaptive classes (`bg-body`, `bg-body-secondary`, `text-body`, `btn-adaptive` — see [css.md](css.md)), `data-lazy="@class animation-slide-up"` animations, `page.resolved.[section]` for frontmatter data, the superheadline/headline/headline_accent/subheadline pattern
|
|
166
|
+
5. Use frontmatter for configuration data, keep actual content in the body
|
|
167
|
+
6. Forms: always FormManager with `onsubmit="return false"` + `novalidate` — see [page-loading.md](page-loading.md) for the full form protection standards
|
|
168
|
+
7. Create `src/assets/css/pages/[page-name]/index.scss` / `src/assets/js/pages/[page-name]/index.js` only if needed ([assets.md](assets.md))
|
|
169
|
+
|
|
170
|
+
A good reference implementation to study: `src/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html` (frontmatter structure, section organization, Bootstrap classes, `{% uj_icon %}`, `page.resolved`, form validation attributes, responsive grids, `data-lazy` animations).
|
|
171
|
+
|
|
172
|
+
## See also
|
|
173
|
+
|
|
174
|
+
- [assets.md](assets.md) — frontmatter customization mechanism, page module/CSS auto-loading, nav/footer/account JSON
|
|
175
|
+
- [seo.md](seo.md) — content writing rules, services/solutions/alternatives page types, JSON-LD schema
|
|
176
|
+
- [themes.md](themes.md) — theme layouts, the classy fallback, authoring new themes
|
|
177
|
+
- [no-inline-scripts.md](no-inline-scripts.md) — where page JS goes (never inline)
|
|
@@ -4,14 +4,7 @@ The local development server URL is stored in `.temp/_config_browsersync.yml` in
|
|
|
4
4
|
|
|
5
5
|
## Log Files
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- `npm start` → `logs/dev.log`
|
|
10
|
-
- `npm run build` (i.e. `UJ_BUILD_MODE=true`) → `logs/build.log`
|
|
11
|
-
|
|
12
|
-
Both files **truncate fresh on each run** — the most recent session only. ANSI color codes are stripped from the file (so it's grep-friendly); the terminal continues to receive colored output unchanged. Captures everything that flows through stdout/stderr: `Manager.logger(...)` output, raw `console.log` calls, gulp task names, jekyll's child output, webpack output, the works.
|
|
13
|
-
|
|
14
|
-
**Skipped on CI/cloud.** When `UJ_IS_SERVER=true` (set by GitHub Actions workflows and other server contexts), the tee is bypassed entirely — no `logs/` directory is written. Implementation: [src/utils/attach-log-file.js](../src/utils/attach-log-file.js), attached at the top of [src/gulp/main.js](../src/gulp/main.js).
|
|
7
|
+
The gulp pipeline tees all output to `logs/dev.log` (`npm start`) / `logs/build.log` (`npm run build`), and `npx mgr test` tees to `logs/test.log`. Full reference — file table, capture behavior, CI skip via `UJ_IS_SERVER`: [logging.md](logging.md).
|
|
15
8
|
|
|
16
9
|
## Connecting to Local Firebase Emulators
|
|
17
10
|
|
package/docs/logging.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Logging
|
|
2
|
+
|
|
3
|
+
UJM tees every line of CLI/pipeline output to log files in the consumer project root, so you can `tail -f` or `grep` a run instead of scrolling terminal scrollback. Frontend runtime logs live in the browser console — this doc covers the file logs UJM itself writes.
|
|
4
|
+
|
|
5
|
+
## Log files
|
|
6
|
+
|
|
7
|
+
All in `<projectRoot>/logs/`:
|
|
8
|
+
|
|
9
|
+
| File | Source | Lifetime |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `dev.log` | Gulp pipeline output on `npm start` | Truncated each run |
|
|
12
|
+
| `build.log` | Gulp pipeline output on `npm run build` (`UJ_BUILD_MODE=true`) | Truncated each run |
|
|
13
|
+
| `test.log` | `npx mgr test` runner output (suite names, pass/fail states, timings) | Truncated each run |
|
|
14
|
+
|
|
15
|
+
`dev.log` and `build.log` are the same gulp tee — which one it writes is chosen by `UJ_BUILD_MODE`, so they never both fill up in one run.
|
|
16
|
+
|
|
17
|
+
## What gets captured
|
|
18
|
+
|
|
19
|
+
Everything that flows through stdout/stderr: `Manager.logger(...)` output, raw `console.log` calls, gulp task names, jekyll's child output, webpack output, the works. ANSI color codes are stripped from the file (grep-friendly); the terminal continues to receive colored output unchanged.
|
|
20
|
+
|
|
21
|
+
## Controls
|
|
22
|
+
|
|
23
|
+
**Skipped on CI/cloud.** When `UJ_IS_SERVER=true` (set by GitHub Actions workflows and other server contexts), the tee is bypassed entirely — no `logs/` directory is written.
|
|
24
|
+
|
|
25
|
+
Implementation: [src/utils/attach-log-file.js](../src/utils/attach-log-file.js), attached at the top of [src/gulp/main.js](../src/gulp/main.js) — same pattern as EM's `dev.log`/`build.log` and BXM's.
|
|
26
|
+
|
|
27
|
+
## See also
|
|
28
|
+
|
|
29
|
+
- [local-development.md](local-development.md) — dev server URL, emulator connection, PurgeCSS
|
|
30
|
+
- [test-framework.md](test-framework.md) — the test runner that feeds `test.log`
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Migration
|
|
2
|
+
|
|
3
|
+
Procedures for moving projects between UJ/UJM format versions. Three modes: **full migration** (old UJ → latest UJM base), **quick fix** (normalize `_config.yml` section order), and **revert posts** (back to the OLD pre-migration format).
|
|
4
|
+
|
|
5
|
+
## Full Migration (old UJ → new UJM)
|
|
6
|
+
|
|
7
|
+
Three stages: repository migration to the latest UJM base (preserving content in `_legacy/`) → config YML re-mapping → cleanup.
|
|
8
|
+
|
|
9
|
+
### Stage 1: Repository migration to the new UJM base
|
|
10
|
+
|
|
11
|
+
1. **Sync latest changes:** `git fetch origin && git pull origin master`
|
|
12
|
+
2. **Delete node_modules** before moving to legacy: `rm -rf node_modules`
|
|
13
|
+
3. **Move everything to `_legacy/`** for a clean slate:
|
|
14
|
+
```bash
|
|
15
|
+
mkdir -p _legacy
|
|
16
|
+
find . -maxdepth 1 ! -name '_legacy' ! -name '.' ! -name '.git' -exec mv {} _legacy/ \;
|
|
17
|
+
```
|
|
18
|
+
4. **Download the Ultimate Jekyll template** (https://github.com/itw-creative-works/ultimate-jekyll):
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/itw-creative-works/ultimate-jekyll.git /tmp/uj-fresh
|
|
21
|
+
rsync -av --exclude='.git' /tmp/uj-fresh/ ./
|
|
22
|
+
rm -rf /tmp/uj-fresh
|
|
23
|
+
```
|
|
24
|
+
5. **Setup and build:** `npm install && npx mgr setup && npm run build`
|
|
25
|
+
6. **Migrate content from legacy.** **DO NOT copy default pages** — UJM provides standard pages through its template system (see `src/defaults/dist/pages` in the UJM package for the full list). Only migrate **content** (posts, images, collections):
|
|
26
|
+
```bash
|
|
27
|
+
# Blog images
|
|
28
|
+
mkdir -p src/assets/images/blog
|
|
29
|
+
cp -r _legacy/assets/_src/images/blog/posts/* src/assets/images/blog/ 2>/dev/null || true
|
|
30
|
+
cp -r _legacy/assets/images/blog/posts/* src/assets/images/blog/ 2>/dev/null || true
|
|
31
|
+
|
|
32
|
+
# Blog posts
|
|
33
|
+
mkdir -p src/_posts
|
|
34
|
+
cp -r _legacy/_posts/* src/_posts/ 2>/dev/null || true
|
|
35
|
+
|
|
36
|
+
# Custom collections / images (if any)
|
|
37
|
+
cp -r _legacy/_themes src/_themes 2>/dev/null || true
|
|
38
|
+
cp -r _legacy/assets/images/themes src/assets/images/ 2>/dev/null || true
|
|
39
|
+
cp -r _legacy/assets/images/resources src/assets/images/ 2>/dev/null || true
|
|
40
|
+
```
|
|
41
|
+
**Custom pages — DO NOT COPY, RECREATE:** reference `_legacy/pages/` for content/text only; rebuild the HTML with the current theme structure ([layouts-and-pages.md](layouts-and-pages.md), [themes.md](themes.md)).
|
|
42
|
+
7. **Verify:** `src/_posts/` populated, `src/assets/images/blog/` populated, `_legacy/_config.yml` exists (used in stage 2), NO legacy pages copied.
|
|
43
|
+
8. **Rename master → main:**
|
|
44
|
+
```bash
|
|
45
|
+
git branch -m master main
|
|
46
|
+
git push -u origin main
|
|
47
|
+
gh api repos/OWNER/REPO -X PATCH -f default_branch=main
|
|
48
|
+
git push origin --delete master
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Stage 2: Config YML re-mapping
|
|
52
|
+
|
|
53
|
+
1. Read the NEW standard template (`src/defaults/src/_config.yml` in the UJM package) and the OLD config (`_legacy/_config.yml`).
|
|
54
|
+
2. Create a migration TODO list.
|
|
55
|
+
3. **Map old keys to new format** — use the NEW format as the template (preserve order, comments, structure); map old keys to new (e.g. `contact.email-support` → `brand.contact.email`). **DO NOT import keys that don't exist in the NEW format**; only import deprecated keys when the user explicitly requests them.
|
|
56
|
+
4. After initial mapping, tell the user: "Base migration complete. If you need any deprecated keys from `_legacy/_config.yml` imported, let me know which ones."
|
|
57
|
+
5. Write the new `src/_config.yml` — NEW structure/order/comments, values filled from the old config where mappings exist.
|
|
58
|
+
6. **Required adjustments:** replace hardcoded brand names with `{{ site.brand.name }}` in `meta.title`/`meta.description`; rewrite `brand.description` to 5-8 words max; set default colors for cookieConsent and chatsy (`#237afc` / `#fff`).
|
|
59
|
+
7. Verify.
|
|
60
|
+
|
|
61
|
+
### Stage 3: Cleanup
|
|
62
|
+
|
|
63
|
+
1. Run `npx mgr migrate`.
|
|
64
|
+
2. Tell the user: the `_legacy/` folder holds the original files for reference and can be deleted (`rm -rf _legacy/`) once confirmed.
|
|
65
|
+
|
|
66
|
+
## Quick Fix (normalize `_config.yml` section order)
|
|
67
|
+
|
|
68
|
+
Ensure these keys exist in `src/_config.yml` in this order. Insert missing keys with these defaults — do NOT overwrite existing values:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
# Tracking
|
|
72
|
+
tracking:
|
|
73
|
+
google-analytics: null
|
|
74
|
+
meta-pixel: null
|
|
75
|
+
tiktok-pixel: null
|
|
76
|
+
|
|
77
|
+
# reCAPTCHA
|
|
78
|
+
recaptcha:
|
|
79
|
+
site-key: null
|
|
80
|
+
|
|
81
|
+
# Cloudflare
|
|
82
|
+
cloudflare:
|
|
83
|
+
zone: null
|
|
84
|
+
|
|
85
|
+
# Download
|
|
86
|
+
download:
|
|
87
|
+
mac:
|
|
88
|
+
universal: ""
|
|
89
|
+
windows:
|
|
90
|
+
universal: ""
|
|
91
|
+
linux:
|
|
92
|
+
debian: ""
|
|
93
|
+
snap: ""
|
|
94
|
+
ios:
|
|
95
|
+
universal: ""
|
|
96
|
+
android:
|
|
97
|
+
universal: ""
|
|
98
|
+
|
|
99
|
+
# Extension
|
|
100
|
+
extension:
|
|
101
|
+
chrome: ""
|
|
102
|
+
firefox: ""
|
|
103
|
+
opera: ""
|
|
104
|
+
safari: ""
|
|
105
|
+
edge: ""
|
|
106
|
+
brave: ""
|
|
107
|
+
|
|
108
|
+
# Favicon
|
|
109
|
+
favicon:
|
|
110
|
+
path: "https://cdn.itwcreativeworks.com/assets/itw-creative-works/images/favicon"
|
|
111
|
+
safari-pinned-tab: "#5bbad5"
|
|
112
|
+
msapp-tile-color: "#da532c"
|
|
113
|
+
theme-color: "#ffffff"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Revert Posts (back to the OLD format)
|
|
117
|
+
|
|
118
|
+
For sites that have NOT yet migrated to the new UJ structure. Verify `./src/_posts` exists before starting.
|
|
119
|
+
|
|
120
|
+
1. **Move posts:** `./src/_posts` → `./_posts` (preserve folder structure)
|
|
121
|
+
2. **Move blog images:** everything under `./src/assets/images/blog/` → `./assets/_src/images/blog/posts/` (create destination if needed)
|
|
122
|
+
3. **Remove `./src`** after the moves complete
|
|
123
|
+
4. **Update front matter** in every `./_posts/**/*.md`:
|
|
124
|
+
- `layout: blueprint/blog/post` → `layout: app/blog/post`
|
|
125
|
+
- rename key `post.description` → `post.excerpt`
|
|
126
|
+
|
|
127
|
+
## See also
|
|
128
|
+
|
|
129
|
+
- [directory-structure.md](directory-structure.md) — where everything lives in the new format
|
|
130
|
+
- [layouts-and-pages.md](layouts-and-pages.md) — recreating custom pages with blueprints
|
|
131
|
+
- [themes.md](themes.md) — the theme structure recreated pages must use
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# NO JavaScript in HTML Files — Hard Rule
|
|
2
|
+
|
|
3
|
+
**THIS IS NON-NEGOTIABLE.** No `<script>` tag with an inline JS body is ever allowed in any file under `src/` — not pages, not components (`_includes/`), not layouts (`_layouts/`). If you find one, you move it. If you write one, you are wrong.
|
|
4
|
+
|
|
5
|
+
## Why this rule exists
|
|
6
|
+
|
|
7
|
+
1. **UJM auto-loads page modules.** The framework already provides a first-class mechanism: `src/assets/js/pages/<pagePath>/index.js` runs automatically for each page based on `data-page-path`. Inline scripts bypass this system, fragment the codebase, and make code impossible to lint/test/bundle properly.
|
|
8
|
+
2. **Inline scripts break module boundaries.** They can't `import` anything, can't be code-split, can't be tree-shaken, and have to re-declare every helper.
|
|
9
|
+
3. **Inline scripts hide bugs.** They often reference globals (`Manager`, `firebase`, `webManager.uj`) that aren't exposed, breaking silently at runtime.
|
|
10
|
+
4. **Inline scripts duplicate logic.** The same drop-down/demo/filter logic gets copy-pasted into many files instead of being shared from a single module.
|
|
11
|
+
5. **Inline scripts are a historical accident.** Old UJM (pre-2.0) exposed `Manager` globally and encouraged inline scripts. That's gone. Any inline script you see in a repo is migration debt that must be paid.
|
|
12
|
+
|
|
13
|
+
## The rule, stated precisely
|
|
14
|
+
|
|
15
|
+
For every `<script>` tag in `src/**/*.html` under a UJM project:
|
|
16
|
+
|
|
17
|
+
| Tag shape | Allowed? | Why |
|
|
18
|
+
|-----------|----------|-----|
|
|
19
|
+
| `<script>...body...</script>` with JS body | ❌ **NEVER** | Move the body to a JS module (see below) |
|
|
20
|
+
| `<script src="https://..."></script>` | ✅ OK | External loader, no inline code |
|
|
21
|
+
| `<script type="application/ld+json">...</script>` | ✅ OK | Structured data, not JS |
|
|
22
|
+
| `<script type="module" src="..."></script>` | ✅ OK | External ES module, no inline code |
|
|
23
|
+
| `<script>` with ≤10 lines of trivial first-paint display | ✅ OK with comment | Small helpers that must run before the bundle |
|
|
24
|
+
|
|
25
|
+
**The "small helper" exception must meet ALL of these:**
|
|
26
|
+
|
|
27
|
+
- Under ~10 lines of code
|
|
28
|
+
- Does a single cosmetic first-paint task (e.g. replacing a `--:--:--` placeholder with the current time)
|
|
29
|
+
- Would cause visible flash/flicker if deferred to the bundle
|
|
30
|
+
- Has an inline comment explaining why it's intentionally inline
|
|
31
|
+
|
|
32
|
+
If in doubt, move it. The exception is for ≤10-line display polyfills, NOT for 20-line "quick" handlers.
|
|
33
|
+
|
|
34
|
+
## Where to move the script
|
|
35
|
+
|
|
36
|
+
### Case 1: The script is inside a **page** file (`src/pages/<something>.html` or `.md`)
|
|
37
|
+
|
|
38
|
+
Move it to `src/assets/js/pages/<pagePath>/index.js` — the path is derived from the page's frontmatter `permalink:`.
|
|
39
|
+
|
|
40
|
+
| Page frontmatter | Page module path |
|
|
41
|
+
|------------------|------------------|
|
|
42
|
+
| `permalink: /` | `src/assets/js/pages/index.js` |
|
|
43
|
+
| `permalink: /contact` | `src/assets/js/pages/contact/index.js` |
|
|
44
|
+
| `permalink: /dashboard/history` | `src/assets/js/pages/dashboard/history/index.js` |
|
|
45
|
+
| `permalink: /tools/form-builder` | `src/assets/js/pages/tools/form-builder/index.js` |
|
|
46
|
+
|
|
47
|
+
**Always check if the target file already exists.** If it does, MERGE your logic into the existing `export default` function. DO NOT overwrite.
|
|
48
|
+
|
|
49
|
+
Module template:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
/**
|
|
53
|
+
* <Page Name> Page JavaScript
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
import webManager from 'web-manager';
|
|
57
|
+
|
|
58
|
+
export default async () => {
|
|
59
|
+
await webManager.dom().ready();
|
|
60
|
+
|
|
61
|
+
// ... moved logic ...
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Case 2: The script is inside a **component** (`src/_includes/**/*.html`)
|
|
66
|
+
|
|
67
|
+
Components are Jekyll partials — they don't have a single "page path" because they can be included in many pages. Two strategies:
|
|
68
|
+
|
|
69
|
+
**Option A — Used by exactly one page:** Move the script to that one page's `src/assets/js/pages/<path>/index.js`. Simpler, more scoped.
|
|
70
|
+
|
|
71
|
+
**Option B — Used by multiple pages (or you can't tell):** Move the script to `src/assets/js/main.js` as an `init<ComponentName>()` function with an **element-existence guard** so it's a no-op on pages without the component.
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
// src/assets/js/main.js
|
|
75
|
+
import Manager from 'ultimate-jekyll-manager';
|
|
76
|
+
import webManager from 'web-manager';
|
|
77
|
+
|
|
78
|
+
const manager = new Manager();
|
|
79
|
+
|
|
80
|
+
manager.initialize().then(() => {
|
|
81
|
+
// Each component gets its own init function with a guard
|
|
82
|
+
initHeroDemo();
|
|
83
|
+
initChatDemo();
|
|
84
|
+
initScheduler();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
function initHeroDemo() {
|
|
88
|
+
// Element-existence guard — MUST be the first thing in the function
|
|
89
|
+
const $toggle = document.getElementById('hero-demo-toggle');
|
|
90
|
+
if (!$toggle) return;
|
|
91
|
+
|
|
92
|
+
// ... moved logic ...
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**How to pick the guard element:** use the first `getElementById` / `querySelector` the original script called. If that element isn't on the page, the original script would have crashed anyway, so returning early is safe.
|
|
97
|
+
|
|
98
|
+
### Case 3: The script is inside a **layout** (`src/_layouts/**/*.html`)
|
|
99
|
+
|
|
100
|
+
Same as components: grep for `layout: <layout-path>` across pages. If one page uses it, move to that page's module. If multiple, move to `main.js` with a guard.
|
|
101
|
+
|
|
102
|
+
## Handling Liquid templating inside a script
|
|
103
|
+
|
|
104
|
+
This is the trickiest case, and the one that trips most people up. **Jekyll Liquid (`{{ ... }}`, `{% ... %}`) runs at BUILD TIME on HTML files. Webpack-bundled JS modules under `src/assets/js/` are NOT processed by Jekyll** — any Liquid you leave inside a `.js` file will appear as literal text in the output.
|
|
105
|
+
|
|
106
|
+
### Strategy A: `data-*` attribute bridge (for Liquid values)
|
|
107
|
+
|
|
108
|
+
Use this when the script reads values from Liquid, e.g. `{{ site.url }}`, `{{ include.action1 | default: "..." }}`, `{{ page.items | jsonify }}`.
|
|
109
|
+
|
|
110
|
+
**Before (broken inline script):**
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<!-- src/_includes/frontend/components/hero-demo.html -->
|
|
114
|
+
<script>
|
|
115
|
+
var platform = '{{ include.platform }}';
|
|
116
|
+
var actions = [
|
|
117
|
+
'{{ include.action1 | default: "Like" }}',
|
|
118
|
+
'{{ include.action2 | default: "Share" }}',
|
|
119
|
+
];
|
|
120
|
+
var items = {{ page.items | jsonify }};
|
|
121
|
+
</script>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**After:**
|
|
125
|
+
|
|
126
|
+
```html
|
|
127
|
+
<!-- src/_includes/frontend/components/hero-demo.html -->
|
|
128
|
+
<!-- Hidden config element — Jekyll fills data attributes at build time -->
|
|
129
|
+
<div id="hero-demo-config"
|
|
130
|
+
data-platform="{{ include.platform }}"
|
|
131
|
+
data-action-1="{{ include.action1 | default: 'Like' }}"
|
|
132
|
+
data-action-2="{{ include.action2 | default: 'Share' }}"
|
|
133
|
+
data-items='{{ page.items | jsonify }}'
|
|
134
|
+
hidden></div>
|
|
135
|
+
|
|
136
|
+
<!-- ... rest of component markup ... -->
|
|
137
|
+
|
|
138
|
+
<!-- Logic moved to src/assets/js/main.js (initHeroDemo) -->
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
// src/assets/js/main.js
|
|
143
|
+
function initHeroDemo() {
|
|
144
|
+
const $config = document.getElementById('hero-demo-config');
|
|
145
|
+
if (!$config) return;
|
|
146
|
+
|
|
147
|
+
const platform = $config.dataset.platform;
|
|
148
|
+
const actions = [
|
|
149
|
+
$config.dataset.action1,
|
|
150
|
+
$config.dataset.action2,
|
|
151
|
+
];
|
|
152
|
+
const items = JSON.parse($config.dataset.items);
|
|
153
|
+
|
|
154
|
+
// ... moved logic ...
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Notes on data attributes:**
|
|
159
|
+
|
|
160
|
+
- Use kebab-case in HTML (`data-action-1`), which maps to camelCase in JS (`.dataset.action1`).
|
|
161
|
+
- For `| jsonify` output, wrap the attribute value in single quotes: `data-items='{{ x | jsonify }}'` (jsonify emits double-quoted JSON).
|
|
162
|
+
- For complex objects, use `| jsonify` → `data-json='...'` → `JSON.parse($config.dataset.json)`.
|
|
163
|
+
|
|
164
|
+
### Strategy B: `<template>` element cloning (for Liquid render tags)
|
|
165
|
+
|
|
166
|
+
Use this when the script interpolates Liquid render tags like `{% uj_icon "name" %}` or `{% include partials/x.html %}` into HTML strings it builds.
|
|
167
|
+
|
|
168
|
+
**Before (broken inline script):**
|
|
169
|
+
|
|
170
|
+
```html
|
|
171
|
+
<!-- Inline script tries to build HTML string with Liquid render tag -->
|
|
172
|
+
<script>
|
|
173
|
+
el.innerHTML = '<span class="icon">{% uj_icon "arrow-pointer", "smaller" %}</span>';
|
|
174
|
+
</script>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**After:**
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<!-- Hidden templates — Jekyll renders the icon markup inside at build time -->
|
|
181
|
+
<template id="hero-demo-icon-arrow-pointer">{% uj_icon "arrow-pointer", "smaller" %}</template>
|
|
182
|
+
<template id="hero-demo-icon-eye">{% uj_icon "eye", "smaller" %}</template>
|
|
183
|
+
|
|
184
|
+
<!-- ... rest of component markup ... -->
|
|
185
|
+
|
|
186
|
+
<!-- Logic moved to src/assets/js/main.js (initHeroDemo) -->
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
// src/assets/js/main.js
|
|
191
|
+
function getIcon(name) {
|
|
192
|
+
const $template = document.getElementById('hero-demo-icon-' + name);
|
|
193
|
+
return $template ? $template.content.cloneNode(true) : document.createTextNode('');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function initHeroDemo() {
|
|
197
|
+
const $container = document.getElementById('hero-demo');
|
|
198
|
+
if (!$container) return;
|
|
199
|
+
|
|
200
|
+
// Build DOM nodes programmatically — NO innerHTML string building
|
|
201
|
+
const $span = document.createElement('span');
|
|
202
|
+
$span.className = 'icon';
|
|
203
|
+
$span.appendChild(getIcon('arrow-pointer'));
|
|
204
|
+
$container.appendChild($span);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Why template cloning instead of innerHTML string concat:** Liquid render tags produce complex nested HTML (SVG with classes, etc.) that can't be easily stringified. Templates let Jekyll render the full HTML once, then JS clones it as needed. This also avoids XSS risk and the need for `escapeHTML()`.
|
|
209
|
+
|
|
210
|
+
### Strategy C: Liquid conditional branching (`{% if %}` in scripts)
|
|
211
|
+
|
|
212
|
+
Rewrite as JS conditionals using data-attribute values.
|
|
213
|
+
|
|
214
|
+
**Before:**
|
|
215
|
+
|
|
216
|
+
```html
|
|
217
|
+
<script>
|
|
218
|
+
{% if include.mode == "turbo" %}
|
|
219
|
+
setInterval(tick, 100);
|
|
220
|
+
{% else %}
|
|
221
|
+
setInterval(tick, 1000);
|
|
222
|
+
{% endif %}
|
|
223
|
+
</script>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**After:**
|
|
227
|
+
|
|
228
|
+
```html
|
|
229
|
+
<div id="demo-config" data-mode="{{ include.mode }}" hidden></div>
|
|
230
|
+
<!-- Logic moved to main.js (initDemo) -->
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
function initDemo() {
|
|
235
|
+
const $config = document.getElementById('demo-config');
|
|
236
|
+
if (!$config) return;
|
|
237
|
+
|
|
238
|
+
const mode = $config.dataset.mode;
|
|
239
|
+
const intervalMs = mode === 'turbo' ? 100 : 1000;
|
|
240
|
+
setInterval(tick, intervalMs);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Handling globals from inline scripts
|
|
245
|
+
|
|
246
|
+
Old inline scripts often defined functions that other code expected to find on `window` (hCaptcha callbacks, YouTube IFrame API callbacks, inline `onclick="..."` handlers, etc.). When moving to a module:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// In the page module / init function
|
|
250
|
+
function hCaptchaLoadCallback() { /* ... */ }
|
|
251
|
+
function hCaptchaCompleteCallback() { /* ... */ }
|
|
252
|
+
|
|
253
|
+
// Expose on window — required by external script that calls it globally
|
|
254
|
+
window.hCaptchaLoadCallback = hCaptchaLoadCallback;
|
|
255
|
+
window.hCaptchaCompleteCallback = hCaptchaCompleteCallback;
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Same applies to `onYouTubeIframeAPIReady`, inline `onclick="handleFoo(this)"`, and similar global-callback patterns. Leave the external `<script src="...">` loader in place (it's allowed), but put its callbacks in a module and explicitly assign them to `window`.
|
|
259
|
+
|
|
260
|
+
## Playbook: moving an inline script step by step
|
|
261
|
+
|
|
262
|
+
1. **Read the full HTML file** (not just the script block) — frontmatter, surrounding markup, all `<script>` tags.
|
|
263
|
+
2. **Classify each `<script>` tag**:
|
|
264
|
+
- `type="application/ld+json"` → leave alone (structured data)
|
|
265
|
+
- `src="..."` → leave alone (external loader)
|
|
266
|
+
- Trivial ≤10-line first-paint helper → leave alone with a comment
|
|
267
|
+
- Everything else → **MOVE**
|
|
268
|
+
3. **Grep the script body** for `{{` and `{%` to find Liquid references. Plan your data-attribute and template element bridges.
|
|
269
|
+
4. **Determine the destination**:
|
|
270
|
+
- Page → `src/assets/js/pages/<pagePath>/index.js` (check if it exists; merge if so)
|
|
271
|
+
- Component/layout used by one page → that page's module
|
|
272
|
+
- Component/layout used by many pages → `src/assets/js/main.js` with `init<Name>()` guard
|
|
273
|
+
5. **Add bridge elements to the HTML** (`<div data-*>` config, `<template>` icons) **before** removing the script. This way Jekyll still processes the Liquid.
|
|
274
|
+
6. **Move the script body** to the destination. Unwrap any `(function() { ... })()` IIFE. Preserve `var`/`const`/`let` exactly. Add the element-existence guard as the first statement.
|
|
275
|
+
7. **Rewrite Liquid references** in the moved JS to read from data attributes or clone templates.
|
|
276
|
+
8. **Replace the `<script>...</script>` block** in HTML with `<!-- Logic moved to <destination path> -->`.
|
|
277
|
+
9. **Verify with `git diff`** — confirm the HTML change is only the script removal + any bridge elements you added.
|
|
278
|
+
10. **Do NOT refactor the script logic** while moving it. Move verbatim with only the changes required to work outside the HTML file. Refactoring and moving in the same pass is how bugs get introduced.
|
|
279
|
+
|
|
280
|
+
## What NOT to do
|
|
281
|
+
|
|
282
|
+
- ❌ Don't leave `Manager.something()` — `Manager` is not globally exposed. Use `import webManager from 'web-manager'`.
|
|
283
|
+
- ❌ Don't leave `firebase.firestore()` — Firebase is not globally exposed either. Use `webManager.firestore()`.
|
|
284
|
+
- ❌ Don't build HTML strings with Liquid interpolation inside a JS module — it won't be processed.
|
|
285
|
+
- ❌ Don't use `window.showExitPopup = () => webManager.uj().showExitPopup()` shims as a way to avoid moving the rest of the logic. Move the whole script.
|
|
286
|
+
- ❌ Don't split one inline script across multiple modules "for organization" — keep it as one function in the destination file.
|
|
287
|
+
- ❌ Don't add the script to `main.js` without an element-existence guard. `main.js` runs on every page.
|
|
288
|
+
- ❌ Don't forget that `src/assets/js/pages/index.js` is the module for the root `permalink: /` — the file lives at `pages/index.js`, NOT `pages//index.js` or `pages/home/index.js`.
|
|
289
|
+
|
|
290
|
+
## Verification
|
|
291
|
+
|
|
292
|
+
After moving an inline script, run this grep to confirm the file is clean:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
# Should find zero matches (excluding ld+json and external src)
|
|
296
|
+
rg -U '<script>[\s\S]*?</script>' src/**/*.html
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## See also
|
|
300
|
+
|
|
301
|
+
- [assets.md](assets.md) — page module structure, webpack aliases, where each JS file lives
|
|
302
|
+
- [common-mistakes.md](common-mistakes.md) — inline scripts are mistake #1
|
|
303
|
+
- [xss-prevention.md](xss-prevention.md) — escaping rules for any HTML the moved JS builds
|
|
304
|
+
- [templating.md](templating.md) — what Liquid processes (and what it doesn't)
|