|
| 1 | +import * as glob from 'glob'; |
| 2 | +import * as gitignore from 'gitignore-parser'; |
| 3 | +import * as fs from 'fs'; |
| 4 | +import * as path from 'path'; |
| 5 | +import * as _ from 'lodash'; |
| 6 | +import * as mkdirp from 'mkdirp'; |
| 7 | +import * as rimraf from 'rimraf'; |
| 8 | + |
| 9 | +const textFileExtensions = ['.gitignore', '.config', '.cs', '.cshtml', 'Dockerfile', '.html', '.js', '.json', '.jsx', '.md', '.ts', '.tsx', '.xproj']; |
| 10 | + |
| 11 | +const templates = { |
| 12 | + 'angular-2': '../../templates/Angular2Spa/', |
| 13 | + 'knockout': '../../templates/KnockoutSpa/', |
| 14 | + 'react-redux': '../../templates/ReactReduxSpa/', |
| 15 | + 'react': '../../templates/ReactSpa/' |
| 16 | +}; |
| 17 | + |
| 18 | +const contentReplacements: { from: RegExp, to: string }[] = [ |
| 19 | + { from: /\bWebApplicationBasic\b/g, to: '<%= namePascalCase %>' }, |
| 20 | + { from: /<ProjectGuid>[0-9a-f\-]{36}<\/ProjectGuid>/g, to: '<ProjectGuid><%= projectGuid %></ProjectGuid>' }, |
| 21 | + { from: /<RootNamespace>.*?<\/RootNamespace>/g, to: '<RootNamespace><%= namePascalCase %></RootNamespace>'}, |
| 22 | + { from: /\s*<BaseIntermediateOutputPath.*?<\/BaseIntermediateOutputPath>/g, to: '' }, |
| 23 | + { from: /\s*<OutputPath.*?<\/OutputPath>/g, to: '' }, |
| 24 | +]; |
| 25 | + |
| 26 | +const filenameReplacements: { from: RegExp, to: string }[] = [ |
| 27 | + { from: /.*\.xproj$/, to: 'tokenreplace-namePascalCase.xproj' } |
| 28 | +]; |
| 29 | + |
| 30 | +function isTextFile(filename: string): boolean { |
| 31 | + return textFileExtensions.indexOf(path.extname(filename).toLowerCase()) >= 0; |
| 32 | +} |
| 33 | + |
| 34 | +function writeFileEnsuringDirExists(root: string, filename: string, contents: string | Buffer) { |
| 35 | + let fullPath = path.join(root, filename); |
| 36 | + mkdirp.sync(path.dirname(fullPath)); |
| 37 | + fs.writeFileSync(fullPath, contents); |
| 38 | +} |
| 39 | + |
| 40 | +function listFilesExcludingGitignored(root: string): string[] { |
| 41 | + let gitIgnorePath = path.join(root, '.gitignore'); |
| 42 | + let gitignoreEvaluator = fs.existsSync(gitIgnorePath) |
| 43 | + ? gitignore.compile(fs.readFileSync(gitIgnorePath, 'utf8')) |
| 44 | + : { accepts: () => true }; |
| 45 | + return glob.sync('**/*', { cwd: root, dot: true, nodir: true }) |
| 46 | + .filter(fn => gitignoreEvaluator.accepts(fn)); |
| 47 | +} |
| 48 | + |
| 49 | +function writeTemplate(sourceRoot: string, destRoot: string) { |
| 50 | + listFilesExcludingGitignored(sourceRoot).forEach(fn => { |
| 51 | + let sourceContent = fs.readFileSync(path.join(sourceRoot, fn)); |
| 52 | + |
| 53 | + // For text files, replace hardcoded values with template tags |
| 54 | + if (isTextFile(fn)) { |
| 55 | + let sourceText = sourceContent.toString('utf8'); |
| 56 | + contentReplacements.forEach(replacement => { |
| 57 | + sourceText = sourceText.replace(replacement.from, replacement.to); |
| 58 | + }); |
| 59 | + |
| 60 | + sourceContent = new Buffer(sourceText, 'utf8'); |
| 61 | + } |
| 62 | + |
| 63 | + // Also apply replacements in filenames |
| 64 | + filenameReplacements.forEach(replacement => { |
| 65 | + fn = fn.replace(replacement.from, replacement.to); |
| 66 | + }); |
| 67 | + |
| 68 | + writeFileEnsuringDirExists(destRoot, fn, sourceContent); |
| 69 | + }); |
| 70 | +} |
| 71 | + |
| 72 | +function copyRecursive(sourceRoot: string, destRoot: string, matchGlob: string) { |
| 73 | + glob.sync(matchGlob, { cwd: sourceRoot, dot: true, nodir: true }) |
| 74 | + .forEach(fn => { |
| 75 | + const sourceContent = fs.readFileSync(path.join(sourceRoot, fn)); |
| 76 | + writeFileEnsuringDirExists(destRoot, fn, sourceContent); |
| 77 | + }); |
| 78 | +} |
| 79 | + |
| 80 | +const outputRoot = './generator-aspnet-spa'; |
| 81 | +const outputTemplatesRoot = path.join(outputRoot, 'app/templates'); |
| 82 | +rimraf.sync(outputTemplatesRoot); |
| 83 | + |
| 84 | +// Copy template files |
| 85 | +_.forEach(templates, (templateRootDir, templateName) => { |
| 86 | + const outputDir = path.join(outputTemplatesRoot, templateName); |
| 87 | + writeTemplate(templateRootDir, outputDir); |
| 88 | +}); |
| 89 | + |
| 90 | +// Also copy the generator files (that's the compiled .js files, plus all other non-.ts files) |
| 91 | +const tempRoot = './tmp'; |
| 92 | +copyRecursive(path.join(tempRoot, 'generator'), outputRoot, '**/*.js'); |
| 93 | +copyRecursive('./src/generator', outputRoot, '**/!(*.ts)'); |
| 94 | + |
| 95 | +// Clean up |
| 96 | +rimraf.sync(tempRoot); |
0 commit comments