Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{
"repo": "owner/web-learning-kit-generator"
}
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Set to true to automatically open BrowserSync in the default browser
BROWSERSYNC_OPEN=false

# Set to true to skip imagemin optimization in the images task
SKIP_IMAGE_OPTIMIZATION=false
15 changes: 15 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Summary
- What changed?
- Why was this needed?

## Validation
- [ ] `npm run typecheck`
- [ ] `npm test`
- [ ] `npm run lint` (if available in environment)

## Generated project impact
- [ ] No generated output behavior changes
- [ ] Generated output behavior changed (describe below)

## Notes
Include any migration or follow-up notes here.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ npm run typecheck
npm test
```

Environment toggles (optional):

```bash
# keep BrowserSync from opening a browser
BROWSERSYNC_OPEN=false

# skip image optimization for faster local loops
SKIP_IMAGE_OPTIMIZATION=true
```

See `.env.example` for supported toggles.

## Project structure

```text
Expand Down Expand Up @@ -151,6 +163,7 @@ If your goal is to make this starter more production-realistic for learners, imp
- Add one-click workflows for GitHub Pages / Netlify / Vercel static output.
2. **Release automation**
- Semantic versioning + changelog generation.
- Track with Changesets config (`.changeset/config.json`).
3. **Performance checks**
- Add Lighthouse CI or static asset budget checks.

Expand Down
84 changes: 51 additions & 33 deletions _gulp/gulpSetup.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,71 @@
import { exec } from 'child_process';
import { spawn } from 'child_process';
import { writeFile } from 'fs/promises';
import { copyVendorCSS, createProjectFiles, createProjectStructure } from './modules/fileSetup';
import { generateGulpfile } from './modules/gulpfileGenerator';
import { confirmProjectDeletion, promptUser } from './modules/setupQuestions';
import { parseSetupOptions } from './modules/setupCliOptions';
import { confirmProjectDeletion, promptUser } from './modules/setupQuestions';
import { assertUserChoices } from './modules/userChoicesValidation';
import { UserChoices } from './types';
import { deleteDirectory, fileExists } from './utils/fileSystem';
import { deleteProjectDirectory, fileExists } from './utils/fileSystem';
import { logger } from './utils/logger';

async function prepareProjectDirectories(autoConfirm: boolean): Promise<boolean> {
const projectExists = fileExists('src') || fileExists('dist');
if (!projectExists) {
return true;
}

const shouldDelete = autoConfirm ? true : await confirmProjectDeletion();
if (!shouldDelete) {
logger.info('Project setup canceled. Exiting...');
return false;
}

deleteProjectDirectory('src');
deleteProjectDirectory('dist');
return true;
}

async function resolveUserChoices(shouldPrompt: boolean, preselectedChoices?: UserChoices): Promise<UserChoices> {
const rawChoices = shouldPrompt ? await promptUser() : preselectedChoices;
return assertUserChoices(rawChoices);
}

async function scaffoldProject(choices: UserChoices): Promise<void> {
await writeFile('_gulp/user-choices.json', JSON.stringify(choices, null, 2));

createProjectStructure(choices);
createProjectFiles(choices);
copyVendorCSS(choices);
generateGulpfile(choices);
}

function startDevServer(): void {
const child = spawn('npm', ['start'], {
stdio: 'inherit',
shell: true,
});

child.on('error', (error) => {
logger.error(`Error starting development server: ${error.message}`);
});
}

async function setup(): Promise<void> {
try {
const parsedOptions = parseSetupOptions(process.argv.slice(2));

const projectExists = fileExists('src') || fileExists('dist');
if (projectExists) {
const shouldDelete = parsedOptions.autoConfirm ? true : await confirmProjectDeletion();
if (!shouldDelete) {
logger.info('Project setup canceled. Exiting...');
return;
}
deleteDirectory('src');
deleteDirectory('dist');
const shouldContinue = await prepareProjectDirectories(parsedOptions.autoConfirm);
if (!shouldContinue) {
return;
}

const rawChoices = parsedOptions.shouldPrompt ? await promptUser() : parsedOptions.choices;
const choices: UserChoices = assertUserChoices(rawChoices);

await writeFile('_gulp/user-choices.json', JSON.stringify(choices, null, 2));

createProjectStructure(choices);
createProjectFiles(choices);
copyVendorCSS(choices);
generateGulpfile(choices);
const choices = await resolveUserChoices(parsedOptions.shouldPrompt, parsedOptions.choices);
await scaffoldProject(choices);

logger.success('Setup complete. Gulpfile has been generated.');
logger.info('Starting development server...');

exec('npm start', (error, stdout, stderr) => {
if (error) {
logger.error(`Error: ${error.message}`);
return;
}
if (stderr) {
logger.error(`Stderr: ${stderr}`);
return;
}
console.log(stdout);
});
startDevServer();
} catch (error: unknown) {
logger.error(`An error occurred during setup: ${(error as Error).message}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ const del = require('del');
const plumber = require('gulp-plumber');
const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = null;
const tsify = null;

const production = process.env.NODE_ENV === 'production';
const openBrowser = process.env.BROWSERSYNC_OPEN === 'true';
const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === 'true';

async function clean() {
await del(['dist']);
Expand Down Expand Up @@ -66,7 +65,7 @@ function markup() {

function images() {
return src('src/img/**/*')
.pipe(imagemin())
.pipe(gulpif(!skipImageOptimization, imagemin()))
.pipe(dest('dist/img'));
}

Expand All @@ -75,7 +74,8 @@ function serve(cb) {
server: {
baseDir: './dist'
},
open: true
open: openBrowser,
notify: false
});
cb();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const plumber = require('gulp-plumber');
const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = require('gulp-pug');
const tsify = null;

const production = process.env.NODE_ENV === 'production';
const openBrowser = process.env.BROWSERSYNC_OPEN === 'true';
const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === 'true';

async function clean() {
await del(['dist']);
Expand Down Expand Up @@ -67,7 +67,7 @@ function markup() {

function images() {
return src('src/img/**/*')
.pipe(imagemin())
.pipe(gulpif(!skipImageOptimization, imagemin()))
.pipe(dest('dist/img'));
}

Expand All @@ -76,7 +76,8 @@ function serve(cb) {
server: {
baseDir: './dist'
},
open: true
open: openBrowser,
notify: false
});
cb();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ const del = require('del');
const plumber = require('gulp-plumber');
const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = null;
const tsify = null;

const production = process.env.NODE_ENV === 'production';
const openBrowser = process.env.BROWSERSYNC_OPEN === 'true';
const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === 'true';

async function clean() {
await del(['dist']);
Expand Down Expand Up @@ -66,7 +65,7 @@ function markup() {

function images() {
return src('src/img/**/*')
.pipe(imagemin())
.pipe(gulpif(!skipImageOptimization, imagemin()))
.pipe(dest('dist/img'));
}

Expand All @@ -75,7 +74,8 @@ function serve(cb) {
server: {
baseDir: './dist'
},
open: true
open: openBrowser,
notify: false
});
cb();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const plumber = require('gulp-plumber');
const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = require('gulp-pug');
const tsify = null;

const production = process.env.NODE_ENV === 'production';
const openBrowser = process.env.BROWSERSYNC_OPEN === 'true';
const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === 'true';

async function clean() {
await del(['dist']);
Expand Down Expand Up @@ -67,7 +67,7 @@ function markup() {

function images() {
return src('src/img/**/*')
.pipe(imagemin())
.pipe(gulpif(!skipImageOptimization, imagemin()))
.pipe(dest('dist/img'));
}

Expand All @@ -76,7 +76,8 @@ function serve(cb) {
server: {
baseDir: './dist'
},
open: true
open: openBrowser,
notify: false
});
cb();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ const del = require('del');
const plumber = require('gulp-plumber');
const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = null;
const tsify = require('tsify');

const production = process.env.NODE_ENV === 'production';
const openBrowser = process.env.BROWSERSYNC_OPEN === 'true';
const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === 'true';

async function clean() {
await del(['dist']);
Expand Down Expand Up @@ -66,7 +66,7 @@ function markup() {

function images() {
return src('src/img/**/*')
.pipe(imagemin())
.pipe(gulpif(!skipImageOptimization, imagemin()))
.pipe(dest('dist/img'));
}

Expand All @@ -75,7 +75,8 @@ function serve(cb) {
server: {
baseDir: './dist'
},
open: true
open: openBrowser,
notify: false
});
cb();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = require('gulp-pug');
const tsify = require('tsify');

const production = process.env.NODE_ENV === 'production';
const openBrowser = process.env.BROWSERSYNC_OPEN === 'true';
const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === 'true';

async function clean() {
await del(['dist']);
Expand Down Expand Up @@ -67,7 +68,7 @@ function markup() {

function images() {
return src('src/img/**/*')
.pipe(imagemin())
.pipe(gulpif(!skipImageOptimization, imagemin()))
.pipe(dest('dist/img'));
}

Expand All @@ -76,7 +77,8 @@ function serve(cb) {
server: {
baseDir: './dist'
},
open: true
open: openBrowser,
notify: false
});
cb();
}
Expand Down
Loading
Loading