--- /dev/null
+pnpm-lock.yaml
+
+# prettier doesn't respect newlines between chained methods
+# https://github.com/prettier/prettier/issues/7884
+**/*.spec.js
+**/*.spec.ts
--- /dev/null
+{
+ "semi": false,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "printWidth": 80,
+ "trailingComma": "none"
+}
# create-vue
+
An easy way to start a Vue project
return !fs.existsSync(dir) || fs.readdirSync(dir).length === 0
}
-function emptyDir (dir) {
+function emptyDir(dir) {
postOrderDirectoryTraverse(
dir,
(dir) => fs.rmdirSync(dir),
async function init() {
const cwd = process.cwd()
const argv = minimist(process.argv.slice(2))
-
+
let targetDir = argv._[0]
const defaultProjectName = !targetDir ? 'vue-project' : targetDir
// - Project language: JavaScript / TypeScript
// - Install Vue Router & Vuex for SPA development?
// - Add Cypress for testing?
- result = await prompts([
- {
- name: 'projectName',
- type: targetDir ? null : 'text',
- message: 'Project name:',
- initial: defaultProjectName,
- onState: (state) =>
- (targetDir = String(state.value).trim() || defaultProjectName)
- },
- {
- name: 'shouldOverwrite',
- type: () => canSafelyOverwrite(targetDir) ? null : 'confirm',
- message: () => {
- const dirForPrompt = targetDir === '.'
- ? 'Current directory'
- : `Target directory "${targetDir}"`
-
- return `${dirForPrompt} is not empty. Remove existing files and continue?`
- }
- },
- {
- name: 'overwriteChecker',
- type: (prev, values = {}) => {
- if (values.shouldOverwrite === false) {
- throw new Error(red('✖') + ' Operation cancelled')
+ result = await prompts(
+ [
+ {
+ name: 'projectName',
+ type: targetDir ? null : 'text',
+ message: 'Project name:',
+ initial: defaultProjectName,
+ onState: (state) =>
+ (targetDir = String(state.value).trim() || defaultProjectName)
+ },
+ {
+ name: 'shouldOverwrite',
+ type: () => (canSafelyOverwrite(targetDir) ? null : 'confirm'),
+ message: () => {
+ const dirForPrompt =
+ targetDir === '.'
+ ? 'Current directory'
+ : `Target directory "${targetDir}"`
+
+ return `${dirForPrompt} is not empty. Remove existing files and continue?`
}
- return null
+ },
+ {
+ name: 'overwriteChecker',
+ type: (prev, values = {}) => {
+ if (values.shouldOverwrite === false) {
+ throw new Error(red('✖') + ' Operation cancelled')
+ }
+ return null
+ }
+ },
+ {
+ name: 'packageName',
+ type: () => (isValidPackageName(targetDir) ? null : 'text'),
+ message: 'Package name:',
+ initial: () => toValidPackageName(targetDir),
+ validate: (dir) =>
+ isValidPackageName(dir) || 'Invalid package.json name'
+ },
+ {
+ name: 'shouldUseTypeScript',
+ type: () => (isValidTemplate ? null : 'toggle'),
+ message: 'Add TypeScript?',
+ initial: false,
+ active: 'Yes',
+ inactive: 'No'
+ },
+ {
+ name: 'isSPA',
+ type: () => (isValidTemplate ? null : 'toggle'),
+ message:
+ 'Add Vue Router & Vuex for Single Page Application development?',
+ initial: false,
+ active: 'Yes',
+ inactive: 'No'
+ },
+ {
+ name: 'shouldAddCypress',
+ type: () => (isValidTemplate ? null : 'toggle'),
+ message: 'Add Cypress for testing?',
+ initial: false,
+ active: 'Yes',
+ inactive: 'No'
}
- },
+ ],
{
- name: 'packageName',
- type: () => (isValidPackageName(targetDir) ? null : 'text'),
- message: 'Package name:',
- initial: () => toValidPackageName(targetDir),
- validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name'
- },
- {
- name: 'shouldUseTypeScript',
- type: () => isValidTemplate ? null : 'toggle',
- message: 'Add TypeScript?',
- initial: false,
- active: 'Yes',
- inactive: 'No'
- },
- {
- name: 'isSPA',
- type: () => isValidTemplate ? null : 'toggle',
- message: 'Add Vue Router & Vuex for Single Page Application development?',
- initial: false,
- active: 'Yes',
- inactive: 'No'
- },
- {
- name: 'shouldAddCypress',
- type: () => isValidTemplate ? null : 'toggle',
- message: 'Add Cypress for testing?',
- initial: false,
- active: 'Yes',
- inactive: 'No'
- }
- ], {
- onCancel: () => {
- throw new Error(red('✖') + ' Operation cancelled')
+ onCancel: () => {
+ throw new Error(red('✖') + ' Operation cancelled')
+ }
}
- })
+ )
} catch (cancelled) {
console.log(cancelled.message)
process.exit(1)
if (filepath.endsWith('.js')) {
fs.renameSync(filepath, filepath.replace(/\.js$/, '.ts'))
} else if (path.basename(filepath) === 'jsconfig.json') {
- fs.renameSync(filepath, filepath.replace(/jsconfig\.json$/, 'tsconfig.json'))
+ fs.renameSync(
+ filepath,
+ filepath.replace(/jsconfig\.json$/, 'tsconfig.json')
+ )
}
}
)
}
// Render code template.
+ // prettier-ignore
const codeTemplate =
(shouldUseTypeScript ? 'typescript-' : '') +
(isSPA ? 'spa' : 'default')
const packageManager = /pnpm/.test(process.env.npm_execpath)
? 'pnpm'
: /yarn/.test(process.env.npm_execpath)
- ?'yarn'
- : 'npm'
-
+ ? 'yarn'
+ : 'npm'
+
const commandsMap = {
install: {
pnpm: 'pnpm install',
"node": "^12.13.0 || ^14.0.0 || >= 16.0.0"
},
"scripts": {
+ "format": "prettier --write .",
"test": "echo \"Error: no test specified\" && exit 1",
"prepublishOnly": "node snapshot.js"
},
"typescript": "~4.3.5",
"vue-tsc": "^0.2.2"
}
-}
\ No newline at end of file
+}
import { ref } from 'vue'
defineProps<{
- msg: String,
+ msg: String
}>()
const count = ref(0)
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"typescript": "~4.3.5",
"vue-tsc": "^0.2.2"
}
-}
\ No newline at end of file
+}
import { ref } from 'vue'
defineProps<{
- msg: String,
+ msg: String
}>()
const count = ref(0)
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
"cypress": "^8.0.0",
"start-server-and-test": "^1.12.6"
}
-}
\ No newline at end of file
+}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
"@vue/compiler-sfc": "^3.1.5",
"vite": "^2.4.3"
}
-}
\ No newline at end of file
+}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"typescript": "~4.3.5",
"vue-tsc": "^0.2.2"
}
-}
\ No newline at end of file
+}
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
- <router-view/>
+ <router-view />
</template>
<style>
import { ref } from 'vue'
defineProps<{
- msg: String,
+ msg: String
}>()
const count = ref(0)
import { createStore } from 'vuex'
export default createStore({
- state: {
- },
- mutations: {
- },
- actions: {
- },
- modules: {
- }
+ state: {},
+ mutations: {},
+ actions: {},
+ modules: {}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"typescript": "~4.3.5",
"vue-tsc": "^0.2.2"
}
-}
\ No newline at end of file
+}
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
- <router-view/>
+ <router-view />
</template>
<style>
import { ref } from 'vue'
defineProps<{
- msg: String,
+ msg: String
}>()
const count = ref(0)
import { createStore } from 'vuex'
export default createStore({
- state: {
- },
- mutations: {
- },
- actions: {
- },
- modules: {
- }
+ state: {},
+ mutations: {},
+ actions: {},
+ modules: {}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
"cypress": "^8.0.0",
"start-server-and-test": "^1.12.6"
}
-}
\ No newline at end of file
+}
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
- <router-view/>
+ <router-view />
</template>
<style>
import { createStore } from 'vuex'
export default createStore({
- state: {
- },
- mutations: {
- },
- actions: {
- },
- modules: {
- }
+ state: {},
+ mutations: {},
+ actions: {},
+ modules: {}
})
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
"@vue/compiler-sfc": "^3.1.5",
"vite": "^2.4.3"
}
-}
\ No newline at end of file
+}
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
- <router-view/>
+ <router-view />
</template>
<style>
import { createStore } from 'vuex'
export default createStore({
- state: {
- },
- mutations: {
- },
- actions: {
- },
- modules: {
- }
+ state: {},
+ mutations: {},
+ actions: {},
+ modules: {}
})
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
import { spawnSync } from 'child_process'
for (const template of templateList) {
- spawnSync('node', [
- new URL('./index.js', import.meta.url).pathname,
- template,
- '--template',
- template
- ], {
- cwd: new URL('./playground/', import.meta.url).pathname
- })
+ spawnSync(
+ 'node',
+ [
+ new URL('./index.js', import.meta.url).pathname,
+ template,
+ '--template',
+ template
+ ],
+ {
+ cwd: new URL('./playground/', import.meta.url).pathname
+ }
+ )
}
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
- <router-view/>
+ <router-view />
</template>
<style>
import { createStore } from 'vuex'
export default createStore({
- state: {
- },
- mutations: {
- },
- actions: {
- },
- modules: {
- }
+ state: {},
+ mutations: {},
+ actions: {},
+ modules: {}
})
import { ref } from 'vue'
defineProps<{
- msg: String,
+ msg: String
}>()
const count = ref(0)
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
- <router-view/>
+ <router-view />
</template>
<style>
import { ref } from 'vue'
defineProps<{
- msg: String,
+ msg: String
}>()
const count = ref(0)
import { createStore } from 'vuex'
export default createStore({
- state: {
- },
- mutations: {
- },
- actions: {
- },
- modules: {
- }
+ state: {},
+ mutations: {},
+ actions: {},
+ modules: {}
})
"resolveJsonModule": true,
"esModuleInterop": true,
"paths": {
- "@/*": [
- "src/*"
- ]
+ "@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"skipLibCheck": true
},
- "include": [
- "vite.config.*",
-
- "src/**/*",
- "src/**/*.vue"
- ],
- "exclude": [
- "src/**/__tests__/**"
- ]
+ "include": ["vite.config.*", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/**"]
}
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@/': new URL('./src/', import.meta.url).pathname,
- },
+ '@/': new URL('./src/', import.meta.url).pathname
+ }
}
})
-const isObject = val => val && typeof val === 'object'
+const isObject = (val) => val && typeof val === 'object'
const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b]))
/**
-import fs from 'fs';
-import path from 'path';
+import fs from 'fs'
+import path from 'path'
export function preOrderDirectoryTraverse(dir, dirCallback, fileCallback) {
for (const filename of fs.readdirSync(dir)) {
- const fullpath = path.resolve(dir, filename);
+ const fullpath = path.resolve(dir, filename)
if (fs.lstatSync(fullpath).isDirectory()) {
dirCallback(fullpath)
// in case the dirCallback removes the directory entirely
}
continue
}
- fileCallback(fullpath);
+ fileCallback(fullpath)
}
}
if (fs.lstatSync(fullpath).isDirectory()) {
postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback)
dirCallback(fullpath)
- continue;
+ continue
}
fileCallback(fullpath)
}
*/
function renderTemplate(src, dest) {
const stats = fs.statSync(src)
-
+
if (stats.isDirectory()) {
// if it's a directory, render its subdirectories and files recusively
fs.mkdirSync(dest, { recursive: true })
if (filename.startsWith('_')) {
// rename `_file` to `.file`
- dest = path.resolve(
- path.dirname(dest),
- filename.replace(/^_/, '.')
- )
+ dest = path.resolve(path.dirname(dest), filename.replace(/^_/, '.'))
}
fs.copyFileSync(src, dest)
const templateList = ['default', 'spa']
- .flatMap(x => [x, x + '-ts'])
- .flatMap(x => [x, x + '-with-tests'])
+ .flatMap((x) => [x, x + '-ts'])
+ .flatMap((x) => [x, x + '-with-tests'])
export default templateList