Three automation mechanisms for efficient migration
Efficient migration through three automation mechanisms:
<!-- Repeated in every page -->
<section class="section-hero">
<div class="picture">
<img src="../../assets/images/welcome/point0.jpeg">
</div>
<div class="content">
<h1><strong>Pure UI</strong></h1>
<p class="desc">Pure UI is a lightweight...</p>
</div>
</section>// 1. Create component data file (comp_hero.js)
export function createHeroComponent() {
return {
name: 'hero',
template: 'comp_hero',
data: { title: 'Pure UI', desc: '...' }
};
}
// 2. Create component template (comp_hero.ejs)
<%_ if (hero) { _%>
<section class="section-hero">
<h1><%= hero.title %></h1>
<p><%= hero.desc %></p>
</section>
<%_ } _%>
// 3. Use in page
const heroComponent = createHeroComponent();Requirement Analysis
↓
Determine Component Name (camelCase)
↓
Create Data File (comp_<name>.js)
├─ Define COMPONENT_NAME
├─ Define COMPONENT_TEMPLATE
├─ Create Data Object
└─ Export Factory Function
↓
Create Template File (comp_<name>.ejs)
├─ Conditional Render Guard
├─ EJS Expressions
└─ Loop Processing
↓
Use in Page Config
└─ Import and Call createXComponent()// server/ejs/comp_hero.js
import config from '../config.js';
import { getImgCdnUrl } from '../../helpers/url.js';
const COMPONENT_NAME = 'hero';
const COMPONENT_TEMPLATE = 'comp_hero';
const heroData = {
image: {
src: getImgCdnUrl(CDN_HOST, '/welcome/point0.jpeg'),
alt: 'Welcome Hero',
},
title: 'Pure UI',
subtitle: 'Client-side rendering framework',
};
export function createHeroComponent() {
return {
name: COMPONENT_NAME,
template: COMPONENT_TEMPLATE,
data: heroData,
};
}Determine Page Name
↓
Update Webpack Config
└─ webpack.config.base.page.js
↓
Create Page File Structure
├─ client/pages/<name>/index.html
├─ client/pages/<name>/index.js
└─ (Optional) Server EJS Template
↓
Update Navigation Links
├─ server/ejs/comp_header.js
└─ client/components/header.html
↓
Page Complete ✅// webpack.config.base.page.js
{
name: 'about', // Auto-generate entry, template, filename
static: true, // Static page marker
}
// Auto-configured:
// - entry: client/pages/about/index.js
// - template: client/pages/about/index.html
// - filename: about.htmlclient/pages/about/
├── index.html # Page HTML (includes header/footer)
└── index.js # Page entry (imports main and scss)// server/configs/aboutPage.js
export default {
name: 'about',
seo: function() {
return {
title: 'About Us',
desc: 'Learn about our company',
};
},
get: async function() {
const headerComponent = createHeaderComponent();
const heroComponent = createHeroComponent();
return {
headerComponent,
heroComponent,
};
},
};data/comps/theme.js (Global Theme)
↓
BaseController (Server-Side)
├─ Read Theme Data
├─ Generate CSS Variables
└─ Inject into <head>
↓
CSS Custom Properties
├─ --color-major-hue
├─ --color-major-saturation
└─ --color-major-lightness
↓
Page Automatically Applies Theme// data/comps/theme.js
export default {
default: {
'--color-major-hue': '140', // Hue (0-360)
'--color-major-saturation': '85%', // Saturation
'--color-major-lightness': '25%', // Lightness
'--logo': 'url("/assets/images/logo.svg")',
},
dark: {
'--color-glb-bg-lightness': '75%', // Dark background
'--color-glb-lightness': '95%', // Dark text
},
};// server/configs/demoPage.js
export default {
name: 'demo',
get: async function() {
return {
data: {
theme: {
default: {
'--color-major-hue': '200', // Override global theme
},
},
},
};
},
};Analyze Old Page
├─ Extract HTML Structure
├─ Identify Dynamic Content
└─ Determine Component Boundaries
↓
Create Reusable Components
├─ Use create-comp skill
├─ Extract data to comp_*.js
└─ Create template comp_*.ejs
↓
Create New Page
├─ Use create-static-page skill
├─ Configure Webpack
└─ Update Navigation Links
↓
Apply Theme
├─ Update data/comps/theme.js
└─ (Optional) Page-level override
↓
Test and Optimize
└─ Verify Functionality
↓
Migration Complete ✅<section class="section-hero">
<div class="picture">
<img src="../../assets/images/welcome/point0.jpeg">
</div>
<div class="content">
<h1><strong>Pure UI</strong></h1>
<p>Client-side rendering framework</p>
</div>
</section>// comp_hero.js - Data Layer
const heroData = {
image: { src: getImgCdnUrl(CDN_HOST, '/welcome/point0.jpeg') },
title: 'Pure UI',
subtitle: 'Client-side rendering framework',
};
// comp_hero.ejs - View Layer
<%_ if (hero) { _%>
<section class="section-hero">
<img src="<%= hero.image.src %>">
<h1><%= hero.title %></h1>
<p><%= hero.subtitle %></p>
</section>
<%_ } _%>Benefits: Data-view separation, reusable across pages, easy updates
Scenario: Create pages with different themes for different brands
// Global Theme (data/comps/theme.js)
export default {
default: { '--color-major-hue': '140' }, // Green theme
};
// Brand A Page
export default {
get: async function() {
return {
data: {
theme: {
default: { '--color-major-hue': '200' }, // Blue theme
},
},
};
},
};
// Brand B Page
export default {
get: async function() {
return {
data: {
theme: {
default: { '--color-major-hue': '4' }, // Red theme
},
},
};
},
};Result: Same component code, different theme configs, rapid brand switching