UNPKG

cs-init

Version:
341 lines (290 loc) 11.8 kB
const fs = require('fs'); const path = require('path'); const { execSync, spawnSync } = require('child_process'); const os = require('os'); const rootDir = process.env.INIT_CWD || process.cwd(); const isWindows = os.platform() === 'win32'; const packageJsonStr = fs.readFileSync(path.resolve(__dirname, 'package.json'), 'utf8'); const packageJson = JSON.parse(packageJsonStr); packageJson.scripts = { "dev": "webpack serve", "build": "webpack", "dns": "node node_modules/cs-init/setup-local-dev.js", "inlinehtml": "codeschmiede-inlinehtml", }; // create package.json const packageJsonPath = path.join(rootDir, 'package.json'); if (!fs.existsSync(packageJsonPath)) { fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); console.log('package.json created:', packageJsonPath); } else { console.log('package.json already exists:', packageJsonPath); } // create webpack.config.js const webpackConfigPath = path.join(rootDir, 'webpack.config.js'); const webpackConfigContent = ` const fs = require('fs'); const path = require('path'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); function readEnv() { const envPath = path.join(__dirname, '.env'); if (!fs.existsSync(envPath)) return {}; return fs.readFileSync(envPath, 'utf8').split('\\n').reduce((acc, line) => { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) return acc; const eq = trimmed.indexOf('='); if (eq === -1) return acc; acc[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim(); return acc; }, {}); } const fileNames = fs.readdirSync('./src').reduce((acc, name) => { return { ...acc, [name.replace('.scss', '')]: './src/' + name }; }, {}); module.exports = () => { const isDev = !!process.env.WEBPACK_SERVE || !!process.env.npm_config_debug; return { mode: isDev ? 'development' : 'production', entry: fileNames, watch: isDev, watchOptions: { ignored: '**/node_modules', }, output: { filename: '[name]', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\\.s?css$/, use: [ isDev ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ], }, ], }, performance: { hints: false, maxEntrypointSize: 512000, maxAssetSize: 512000 }, optimization: { minimizer: [ new TerserPlugin({ terserOptions: { compress: { drop_console: !isDev, }, }, }), new CssMinimizerPlugin(), ], usedExports: true, }, plugins: [ ...(!isDev ? [ new MiniCssExtractPlugin({ filename: '[name].css', }) ] : []), { apply: (compiler) => { compiler.hooks.done.tap('CleanDistPlugin', (stats) => { if (isDev) return; const distPath = path.resolve(compiler.options.output.path); fs.readdir(distPath, (err, files) => { if (err) throw err; for (const file of files) { if (file.indexOf('.') === -1) { fs.unlink(path.join(distPath, file), (err) => { if (err) throw err; }); } } }); }); } } ], devServer: { allowedHosts: "all", headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization" }, static: [ { directory: path.join(__dirname, 'assets'), publicPath: '/assets', }, { directory: path.join(__dirname, 'dist'), }, ], hot: false, client: false, compress: true, port: 8080, server: (() => { if (!isDev) return {}; const env = readEnv(); const sslEnabled = env.CS_DEV_SSL === 'true'; const certsExist = fs.existsSync('./certs/server.key') && fs.existsSync('./certs/server.crt'); if (sslEnabled && certsExist) { return { type: 'https', options: { key: fs.readFileSync('./certs/server.key'), cert: fs.readFileSync('./certs/server.crt'), }, }; } return { type: 'http' }; })() } } }; `; if (!fs.existsSync(webpackConfigPath)) { fs.writeFileSync(webpackConfigPath, webpackConfigContent.trim()); console.log('webpack.config.js created:', webpackConfigPath); } else { console.log('webpack.config.js already exists:', webpackConfigPath); } // create certs dir (certs themselves are created by setup or the fallback below) const certsPath = path.join(rootDir, 'certs'); const keyPath = path.join(certsPath, 'server.key'); const certPath = path.join(certsPath, 'server.crt'); const envPath = path.join(rootDir, '.env'); if (!fs.existsSync(certsPath)) { fs.mkdirSync(certsPath); } // create src const srcDir = path.join(rootDir, 'src'); if (!fs.existsSync(srcDir)) { fs.mkdirSync(srcDir); console.log('Directory created:', srcDir); } else { console.log('Directory already exists:', srcDir); } const variation01JsContent = ` import './variation-01.scss'; import { waitFor, qs } from 'codeschmiede-toolkit'; (() => { console.log('V1'); waitFor('body', (body) => { if(!body) return; }); })(); `; const srcScript = path.join(srcDir, 'variation-01.js'); if (!fs.existsSync(srcScript)) { fs.writeFileSync(srcScript, variation01JsContent); console.log('variation-01.js created:', srcScript); } else { console.log('variation-01.js already exists:', srcScript); } const srcStyle = path.join(srcDir, 'variation-01.scss'); if (!fs.existsSync(srcStyle)) { fs.writeFileSync(srcStyle, ''); console.log('variation-01.scss created:', srcStyle); } else { console.log('variation-01.scss already exists:', srcStyle); } // create assets const assetsDir = path.join(rootDir, 'assets'); if (!fs.existsSync(assetsDir)) { fs.mkdirSync(assetsDir); console.log('Directory created:', assetsDir); } else { console.log('Directory already exists:', assetsDir); } // ─── README ─────────────────────────────────────────────────────────────────── const readmePath = path.join(rootDir, 'README.md'); const readmeContent = `# AB-Test Setup ## Dev-Server starten \`\`\`bash npm run dev \`\`\` Erreichbar unter **https://localhost:8080** (HTTPS wird automatisch beim Setup eingerichtet). ## Scripts | Command | Beschreibung | |---|---| | \`npm run dev\` | Startet den Webpack Dev-Server | | \`npm run build\` | Produktions-Build nach \`dist/\` | | \`npm run dns\` | Richtet eine Custom-Domain ein (DNS-Eintrag + Zertifikat) | | \`npm run inlinehtml\` | Inlined JS/CSS in eine HTML-Datei | ## Custom-Domain einrichten (optional) Mit \`npm run dns\` kannst du eine lokale Domain wie \`local.codeschmiede.de\` einrichten. Das Script legt automatisch einen Eintrag in \`/etc/hosts\` an und erstellt ein HTTPS-Zertifikat. > **macOS-Hinweis:** Verwende keine \`.local\`-Domain — diese werden über mDNS/Bonjour aufgelöst > und können zu bis zu 30s Verzögerungen führen. Das Script warnt dich automatisch. ## Projektstruktur \`\`\` src/ → JS- und SCSS-Quelldateien (variation-01.js, variation-01.scss, ...) dist/ → Compiled Output (wird von webpack generiert) assets/ → Statische Dateien (werden unter /assets ausgeliefert) certs/ → SSL-Zertifikate (automatisch generiert, nicht committen) \`\`\` ## Neue Variation anlegen Neue Dateien in \`src/\` werden automatisch von webpack als eigener Entry erkannt — einfach \`variation-02.js\` anlegen und \`npm run dev\` neu starten. `; if (!fs.existsSync(readmePath)) { fs.writeFileSync(readmePath, readmeContent); console.log('README.md created:', readmePath); } // ─── Localhost HTTPS cert ───────────────────────────────────────────────────── function commandExists(cmd) { try { const r = spawnSync(isWindows ? 'where' : 'which', [cmd], { stdio: 'pipe' }); return r.status === 0; } catch { return false; } } function writeEnvFlag() { let content = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : ''; if (!/^CS_DEV_SSL=/m.test(content)) { content = content ? `${content.trimEnd()}\nCS_DEV_SSL=true\n` : `CS_DEV_SSL=true\n`; fs.writeFileSync(envPath, content); } } function generateLocalhostCert() { if (!fs.existsSync(certsPath)) fs.mkdirSync(certsPath, { recursive: true }); if (commandExists('mkcert')) { spawnSync('mkcert', ['-install'], { stdio: 'pipe' }); const r = spawnSync('mkcert', ['-key-file', keyPath, '-cert-file', certPath, 'localhost', '127.0.0.1'], { stdio: 'pipe' }); if (r.status === 0) return 'mkcert'; } if (commandExists('openssl')) { try { execSync( `openssl req -x509 -newkey rsa:2048 -nodes -keyout "${keyPath}" -out "${certPath}" -days 3650 -subj "/CN=localhost/O=cs-init/C=DE" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"`, { stdio: 'pipe' } ); return 'openssl'; } catch { return null; } } return null; } const certResult = generateLocalhostCert(); if (certResult) writeEnvFlag(); // ─── Done ───────────────────────────────────────────────────────────────────── const protocol = certResult ? 'https' : 'http'; const certNote = certResult === 'mkcert' ? '(mkcert, trusted)' : certResult === 'openssl' ? '(openssl, Browserwarnung möglich)' : '(kein openssl/mkcert gefunden)'; console.log('\n---------------------------------------------'); console.log(' cs-init · Setup abgeschlossen'); console.log('---------------------------------------------'); console.log(` Dev-Server starten: npm run dev`); console.log(` Erreichbar unter: ${protocol}://localhost:8080 ${certResult ? certNote : ''}`); console.log(''); console.log(' Custom-Domain einrichten (optional):'); console.log(' DNS-Eintrag + Zertifikat: npm run dns'); console.log('---------------------------------------------\n');