A system for building static webapps
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: use remote files if remote

+98 -74
+97 -73
packages/cli/commands/init.ts
··· 1 1 import { Command, EnumType } from '@cliffy/command' 2 2 import { Input, Select } from '@cliffy/prompt' 3 - import { ensureDir, exists, walk } from '@std/fs' 4 - import { dirname, join, relative, resolve } from '@std/path' 3 + import { ensureDir, exists } from '@std/fs' 4 + import { dirname, join, resolve } from '@std/path' 5 5 import { logError, logInfo, logSuccess, theme } from '../utils/ui.ts' 6 6 import { 7 7 type ProjectType, ··· 19 19 extension: 'Browser extension (Chrome MV3 + Firefox MV2)', 20 20 } 21 21 22 + // Static manifest of all files per stub type. 23 + // Must be updated when stub files are added or removed. 24 + const STUB_FILES: Record<ProjectType, string[]> = { 25 + pwa: [ 26 + '.gitignore', 27 + 'civility.json', 28 + 'deno.json', 29 + 'deno.lock', 30 + 'README.md', 31 + 'www/index.d.ts', 32 + 'www/index.html', 33 + 'www/index.ts', 34 + 'www/manifest.json', 35 + 'www/routes/clock.ts', 36 + 'www/routes/stopwatch.ts', 37 + 'www/static/civility.css', 38 + 'www/static/icon.svg', 39 + 'www/static/icons/clock.svg', 40 + 'www/static/icons/download.svg', 41 + 'www/static/icons/watch.svg', 42 + 'www/static/icons/x.svg', 43 + 'www/static/theme.css', 44 + 'www/static/utilities.css', 45 + 'www/utils/updates.ts', 46 + 'www/worker.js', 47 + 'www/worker.ts', 48 + ], 49 + blog: [ 50 + '.gitignore', 51 + 'civility.json', 52 + 'deno.json', 53 + 'README.md', 54 + 'md/my_first_post.md', 55 + 'www/blog.css', 56 + 'www/template.html', 57 + ], 58 + docs: [ 59 + '.gitignore', 60 + 'civility.json', 61 + 'deno.json', 62 + 'README.md', 63 + 'docs.css', 64 + 'index.md', 65 + 'template.html', 66 + ], 67 + extension: [ 68 + '.gitignore', 69 + 'civility.json', 70 + 'deno.json', 71 + 'README.md', 72 + 'www/background.ts', 73 + 'www/content_script.ts', 74 + 'www/manifest.json', 75 + 'www/options.html', 76 + 'www/options.ts', 77 + 'www/popup.html', 78 + 'www/popup.ts', 79 + 'www/static/icon.svg', 80 + ], 81 + } 82 + 22 83 const projectTypeType = new EnumType(PROJECT_TYPES) 23 84 24 85 export const Init = new Command() ··· 72 133 Deno.exit(1) 73 134 } 74 135 75 - const stubDir = decodeURI( 76 - new URL(`../stubs/${type}/`, import.meta.url).pathname, 77 - ) 78 - 79 - if (!await exists(stubDir)) { 80 - logError(`Stub template not found at: ${stubDir}`) 81 - logError('The civility CLI installation may be corrupted.') 82 - Deno.exit(1) 83 - } 84 - 85 - // Validate required files based on project type 86 - const requiredFiles = type === 'pwa' 87 - ? [ 88 - 'www/index.html', 89 - 'www/manifest.json', 90 - 'deno.json', 91 - 'README.md', 92 - 'civility.json', 93 - ] 94 - : type === 'blog' 95 - ? [ 96 - 'md/my_first_post.md', 97 - 'www/template.html', 98 - 'deno.json', 99 - 'README.md', 100 - 'civility.json', 101 - ] 102 - : type === 'extension' 103 - ? [ 104 - 'www/manifest.json', 105 - 'www/background.ts', 106 - 'www/popup.ts', 107 - 'www/popup.html', 108 - 'deno.json', 109 - 'README.md', 110 - 'civility.json', 111 - ] 112 - : [ 113 - 'www/index.md', 114 - 'www/template.html', 115 - 'deno.json', 116 - 'README.md', 117 - 'civility.json', 118 - ] 119 - 120 - for (const file of requiredFiles) { 121 - const filePath = join(stubDir, file) 122 - if (!await exists(filePath)) { 123 - logError(`Required template file missing: ${file}`) 124 - logError('The civility CLI installation may be corrupted.') 125 - Deno.exit(1) 126 - } 127 - } 136 + const stubBaseUrl = new URL(`../stubs/${type}/`, import.meta.url) 137 + const files = STUB_FILES[type] 128 138 129 139 logInfo( 130 140 `Creating new Civility ${theme.bold(type)} project: ${ ··· 137 147 138 148 await ensureDir(targetDir) 139 149 140 - const filesToProcess: string[] = [] 141 - for await (const entry of walk(stubDir, { includeDirs: false })) { 142 - const relativePath = relative(stubDir, entry.path) 143 - const targetPath = join(targetDir, relativePath) 150 + const textFiles: string[] = [] 144 151 145 - await ensureDir(dirname(targetPath)) 146 - await Deno.copyFile(entry.path, targetPath) 152 + for (const relPath of files) { 153 + const sourceUrl = new URL(relPath, stubBaseUrl) 154 + const targetPath = join(targetDir, relPath) 147 155 148 - if (shouldProcessAsText(entry.path)) filesToProcess.push(targetPath) 149 - } 150 - 151 - for (const filePath of filesToProcess) { 156 + let bytes: Uint8Array 152 157 try { 153 - const content = await Deno.readTextFile(filePath) 154 - const processedContent = replaceTemplateVars(content, { 155 - name: name, 156 - url: url, 157 - }) 158 - 159 - await Deno.writeTextFile(filePath, processedContent) 158 + bytes = await readStubFile(sourceUrl) 160 159 } catch (error) { 161 160 logError( 162 - `Failed to process ${filePath}: ${ 161 + `Stub file missing: ${relPath} — ${ 163 162 error instanceof Error ? error.message : String(error) 164 163 }`, 165 164 ) 165 + logError('The civility CLI installation may be corrupted.') 166 + Deno.exit(1) 166 167 } 168 + 169 + await ensureDir(dirname(targetPath)) 170 + await Deno.writeFile(targetPath, bytes) 171 + 172 + if (shouldProcessAsText(relPath)) textFiles.push(targetPath) 173 + } 174 + 175 + for (const filePath of textFiles) { 176 + const content = await Deno.readTextFile(filePath) 177 + await Deno.writeTextFile( 178 + filePath, 179 + replaceTemplateVars(content, { name, url }), 180 + ) 167 181 } 168 182 169 183 // Format generated project with default deno fmt (no workspace config) ··· 202 216 Deno.exit(1) 203 217 } 204 218 }) 219 + 220 + // Reads a stub file via filesystem (file://) or fetch (https://). 221 + async function readStubFile(url: URL): Promise<Uint8Array> { 222 + if (url.protocol === 'file:') { 223 + return await Deno.readFile(url) 224 + } 225 + const res = await fetch(url) 226 + if (!res.ok) throw new Error(`${res.status} ${res.statusText}: ${url}`) 227 + return new Uint8Array(await res.arrayBuffer()) 228 + }
+1 -1
packages/cli/deno.json
··· 1 1 { 2 2 "name": "@civility/cli", 3 - "version": "0.2.1", 3 + "version": "0.2.2", 4 4 "exports": { 5 5 ".": "./main.ts" 6 6 },