diff --git a/.github/ISSUE_TEMPLATE/bug-report--quasar-v1-.md b/.github/ISSUE_TEMPLATE/bug-report--quasar-v1-.md index 93e152c03f6..8115a7d4eb6 100644 --- a/.github/ISSUE_TEMPLATE/bug-report--quasar-v1-.md +++ b/.github/ISSUE_TEMPLATE/bug-report--quasar-v1-.md @@ -11,7 +11,7 @@ assignees: '' A clear and concise description of what the bug is. **Codepen/jsFiddle/Codesandbox (required)** -Fork a Codepen (https://codepen.quasar.dev) or a jsFiddle (https://jsfiddle.quasar.dev) or a Codesandbox (https://codesandbox.quasar.dev) and hit save then copy-paste link here. +Fork a Codepen (https://codepen.io/rstoenescu/pen/VgQbdx) or a jsFiddle (https://jsfiddle.net/rstoenescu/rmaodk0f) or a Codesandbox (https://codesandbox.io/s/github/quasarframework/quasar-codesandbox/tree/legacy-v1) and hit save then copy-paste link here. **To Reproduce** Steps to reproduce the behavior: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e2aeef1a6b5..d96386bd0a0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,11 +25,11 @@ If yes, please describe the impact and migration path for existing applications: **The PR fulfills these requirements:** -- [ ] It's submitted to the `dev` branch and _not_ the `master` branch +- [ ] It's submitted to the `v1` branch - [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix: #xxx[,#xxx]`, where "xxx" is the issue number) - [ ] It's been tested on a Cordova (iOS, Android) app - [ ] It's been tested on a Electron app -- [ ] Any necessary documentation has been added or updated [in the docs](https://github.com/quasarframework/quasar/tree/dev/docs) (for faster update click on "Suggest an edit on GitHub" at bottom of page) or explained in the PR's description. +- [ ] Any necessary documentation has been added or updated [in the docs](https://github.com/quasarframework/quasar/tree/v1/docs) (for faster update click on "Suggest an edit on GitHub" at bottom of page) or explained in the PR's description. If adding a **new feature**, the PR's description includes: - [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index babd806f3b4..665679604ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ Hi! I’m really excited that you are interested in contributing to Quasar. Befo - For more complicated questions, you can use [the official forum](https://forum.quasar.dev/). Make sure to provide enough information when asking your questions - this makes it easier for others to help you! -- Try to search for your issue, it may have already been answered or even fixed in the development branch (`dev`). +- Try to search for your issue, it may have already been answered or even fixed in the development branch (`v1`). - Check if the issue is reproducible with the latest stable version of Quasar. If you are using a pre-release, please indicate the specific version you are using. @@ -33,9 +33,7 @@ Hi! I’m really excited that you are interested in contributing to Quasar. Befo ## Pull Request Guidelines -- The `master` branch is basically just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.** - -- Checkout a topic branch from the relevant branch, e.g. `dev`, and merge back against that branch. +- Checkout a topic branch from the relevant branch, e.g. `v1`, and merge back against that branch. - **DO NOT** checkin `dist` in the commits. diff --git a/app/lib/app-extension/Extension.js b/app/lib/app-extension/Extension.js index 9aa49d24cc3..386c9643bf8 100644 --- a/app/lib/app-extension/Extension.js +++ b/app/lib/app-extension/Extension.js @@ -97,7 +97,7 @@ async function renderFolders ({ source, rawCopy, scope }) { } } - renderFile({ sourcePath, targetPath, rawCopy, scope }) + await renderFile({ sourcePath, targetPath, rawCopy, scope }) } } diff --git a/app/lib/webpack/create-chain.js b/app/lib/webpack/create-chain.js index 80caca5daad..ffb37f2c465 100644 --- a/app/lib/webpack/create-chain.js +++ b/app/lib/webpack/create-chain.js @@ -170,7 +170,13 @@ module.exports = function (cfg, configName) { .plugin('ts-checker') // https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options .use(ForkTsCheckerWebpackPlugin, [ - { ...(cfg.supportTS.tsCheckerConfig || {}), vue: true } + merge({}, cfg.supportTS.tsCheckerConfig || {}, { + typescript: { + extensions: { + vue: true + } + } + }) ]) } diff --git a/app/package.json b/app/package.json index c2b2a39019a..d04d6708ab9 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "@quasar/app", - "version": "2.2.10", + "version": "2.4.4", "description": "Quasar Framework local CLI", "bin": { "quasar": "./bin/quasar" @@ -43,20 +43,20 @@ }, "dependencies": { "@quasar/babel-preset-app": "2.0.1", - "@quasar/fastclick": "1.1.4", + "@quasar/fastclick": "1.1.5", "@types/cordova": "0.0.34", - "@types/electron-packager": "14.0.0", - "@types/express": "4.17.11", - "@types/lru-cache": "5.1.0", - "@types/terser-webpack-plugin": "4.2.1", - "@types/webpack": "4.41.27", - "@types/webpack-bundle-analyzer": "3.9.2", - "@types/webpack-dev-server": "3.11.3", - "archiver": "5.3.0", + "@types/electron-packager": "^14.0.0", + "@types/express": "^4.17.13", + "@types/lru-cache": "^5.1.0", + "@types/terser-webpack-plugin": "^4.2.1", + "@types/webpack": "^4.41.27", + "@types/webpack-bundle-analyzer": "^3.9.2", + "@types/webpack-dev-server": "^3.11.3", + "archiver": "5.3.1", "autoprefixer": "9.8.6", - "browserslist": "^4.14.7", + "browserslist": "^4.20.2", "chalk": "4.1.1", - "chokidar": "3.5.1", + "chokidar": "^3.5.3", "ci-info": "2.0.0", "compression-webpack-plugin": "5.0.1", "copy-webpack-plugin": "6.3.2", @@ -65,17 +65,17 @@ "cssnano": "4.1.10", "dot-prop": "5.3.0", "elementtree": "0.1.7", - "express": "4.17.1", - "fast-glob": "3.2.5", + "express": "4.17.3", + "fast-glob": "3.2.11", "file-loader": "6.2.0", - "fork-ts-checker-webpack-plugin": "4.1.6", + "fork-ts-checker-webpack-plugin": "^6.0.0", "friendly-errors-webpack-plugin": "1.7.0", "fs-extra": "9.0.1", "html-minifier": "4.0.0", "html-webpack-plugin": "4.5.0", "inquirer": "7.3.3", "isbinaryfile": "4.0.8", - "launch-editor-middleware": "2.2.1", + "launch-editor-middleware": "2.3.0", "lodash.debounce": "4.0.8", "lodash.template": "4.5.0", "lodash.throttle": "4.1.1", @@ -83,11 +83,11 @@ "lru-cache": "6.0.0", "memory-fs": "0.5.0", "mini-css-extract-plugin": "0.11.2", - "minimist": "1.2.5", + "minimist": "1.2.6", "node-loader": "1.0.2", "open": "7.1.0", "optimize-css-assets-webpack-plugin": "5.0.4", - "ouch": "2.0.0", + "ouch": "2.0.1", "postcss-loader": "3.0.0", "postcss-rtl": "1.7.3", "postcss-safe-parser": "4.0.2", @@ -99,27 +99,25 @@ "strip-ansi": "6.0.0", "stylus": "0.54.8", "stylus-loader": "3.0.2", - "table": "6.7.1", + "table": "6.8.0", "terser-webpack-plugin": "4.2.2", "ts-loader": "8.0.17", "typescript": "4.2.2", "url-loader": "4.1.1", - "vue": "2.6.14", - "vue-loader": "15.9.7", - "vue-router": "3.5.1", - "vue-server-renderer": "2.6.14", - "vue-style-loader": "4.1.3", - "vue-template-compiler": "2.6.14", - "vuex": "3.6.2", - "webpack": "4.44.2", - "webpack-bundle-analyzer": "4.4.2", - "webpack-chain": "6.5.1", - "webpack-dev-server": "3.11.2", - "webpack-merge": "4.2.2", - "webpack-node-externals": "2.5.2", - "write-file-webpack-plugin": "4.5.1", - "yargs": "15.4.1", - "zlib": "1.0.5" + "vue": "^2.7.1", + "vue-loader": "^15.10.0", + "vue-router": "^3.6.5", + "vue-server-renderer": "^2.7.1", + "vue-style-loader": "^4.1.3", + "vue-template-compiler": "^2.7.1", + "vuex": "^3.6.2", + "webpack": "^4.44.2", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-chain": "^6.5.1", + "webpack-dev-server": "^3.11.3", + "webpack-merge": "^4.2.2", + "webpack-node-externals": "^2.5.2", + "write-file-webpack-plugin": "4.5.1" }, "engines": { "node": ">= 10.0.0", diff --git a/app/templates/capacitor/www/index.html b/app/templates/capacitor/www/index.html index 6c1380c79f8..07b4f60ecf5 100644 --- a/app/templates/capacitor/www/index.html +++ b/app/templates/capacitor/www/index.html @@ -9,7 +9,7 @@ - + + +``` + +## Vue CLI project + +```js +import Vue from 'vue' +import Plugin from 'quasar-ui-<%= name %>' +import 'quasar-ui-<%= name %>/dist/index.css' + +Vue.use(Plugin) +``` + +**OR**: + +```html + + + +``` + +## UMD variant + +Exports `window.<%= umdExportName %>`. + +Add the following tag(s) after the Quasar ones: + +```html + + + + + + + + +``` +If you need the RTL variant of the CSS, then go for the following (instead of the above stylesheet link): +```html + +``` + +# Setup +```bash +$ yarn +``` + +# Developing +```bash +# start dev in SPA mode +$ yarn dev + +# start dev in UMD mode +$ yarn dev:umd + +# start dev in SSR mode +$ yarn dev:ssr + +# start dev in Cordova iOS mode +$ yarn dev:ios + +# start dev in Cordova Android mode +$ yarn dev:android + +# start dev in Electron mode +$ yarn dev:electron +``` + +# Building package +```bash +$ yarn build +``` + +# Adding Testing Components +in the `ui/dev/src/pages` you can add Vue files to test your component/directive. When using `yarn dev` to build the UI, any pages in that location will automatically be picked up by dynamic routing and added to the test page. + +# Adding Assets +If you have a component that has assets, like language or icon-sets, you will need to provide these for UMD. In the `ui/build/script.javascript.js` file, you will find a couple of commented out commands that call `addAssets`. Uncomment what you need and add your assets to have them be built and put into the `ui/dist` folder. + +# Donate +If you appreciate the work that went into this, please consider [donating to Quasar](https://donate.quasar.dev). + +# License +<%= license %> (c) <%= author %> diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/_.npmignore b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/_.npmignore new file mode 100644 index 00000000000..c8b033b748f --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/_.npmignore @@ -0,0 +1,21 @@ +/build +/dev +umd-test.html + +.DS_Store +.thumbs.db +yarn.lock +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +.editorconfig +.eslintignore +.eslintrc.js diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/_package.json b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/_package.json new file mode 100644 index 00000000000..18dfadbcc1e --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/_package.json @@ -0,0 +1,41 @@ +{ + "name": "quasar-ui-<%= name %>", + "version": "0.0.1", + "author": "<%= author %>", + "description": "<%= packageDescription %>", + "license": "<%= license %>", + "module": "dist/index.esm.js", + "main": "dist/index.common.js", + "scripts": { + "dev": "cd dev && yarn dev && cd ..", + "dev:umd": "yarn build && node build/script.open-umd.js", + "dev:ssr": "cd dev && yarn 'dev:ssr' && cd ..", + "dev:ios": "cd dev && yarn 'dev:ios' && cd ..", + "dev:android": "cd dev && yarn 'dev:android' && cd ..", + "dev:electron": "cd dev && yarn 'dev:electron' && cd ..", + "build": "node build/index.js", + "build:js": "node build/script.javascript.js", + "build:css": "node build/script.css.js" + }, + "devDependencies": { + "autoprefixer": "^10.0.2", + "cssnano": "^4.1.10", + "postcss": "^8.1.9", + "rtlcss": "^2.6.1", + "sass": "1.32.0", + "quasar": "^1.0.0", + "open": "^7.3.0", + "fs-extra": "^8.1.0", + "chalk": "^4.1.0", + "rimraf": "^3.0.0", + "rollup": "^2.33.3", + "@rollup/plugin-buble": "^0.20.0", + "@rollup/plugin-json": "^4.0.0", + "@rollup/plugin-node-resolve": "^10.0.0", + "uglify-es": "^3.3.9", + "zlib": "^1.0.5" + }, + "browserslist": [ + "last 1 version, not dead, ie >= 11" + ] +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/config.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/config.js new file mode 100644 index 00000000000..c997ed50443 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/config.js @@ -0,0 +1,13 @@ +const { name, author, version } = require('../package.json') +const year = (new Date()).getFullYear() + +module.exports = { + name, + version, + banner: + '/*!\n' + + ' * ' + name + ' v' + version + '\n' + + ' * (c) ' + year + ' ' + author + '\n' + + ' * Released under the MIT License.\n' + + ' */\n' +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.common.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.common.js new file mode 100644 index 00000000000..004c8921656 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.common.js @@ -0,0 +1,3 @@ +import Plugin from '../../src/index' + +export default Plugin diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.esm.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.esm.js new file mode 100644 index 00000000000..770e53769c0 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.esm.js @@ -0,0 +1,4 @@ +import Plugin from '../../src/index' + +export default Plugin +export * from '../../src/index' diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.umd.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.umd.js new file mode 100644 index 00000000000..665df575691 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/entry/index.umd.js @@ -0,0 +1,6 @@ +import Vue from 'vue' +import Plugin from '../../src/index' + +Vue.use(Plugin) + +export * from '../../src/index' diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/index.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/index.js new file mode 100644 index 00000000000..dd6ca18e030 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/index.js @@ -0,0 +1,19 @@ +process.env.NODE_ENV = 'production' + +const parallel = require('os').cpus().length > 1 +const runJob = parallel ? require('child_process').fork : require +const { join } = require('path') +const { createFolder } = require('./utils') +const { green, blue } = require('chalk') + +console.log() + +<% if (features.ae) { %>require('./script.app-ext.js').syncAppExt()<% } %> +require('./script.clean.js') + +console.log(` 📦 Building ${green('v' + require('../package.json').version)}...${parallel ? blue(' [multi-threaded]') : ''}\n`) + +createFolder('dist') + +runJob(join(__dirname, './script.javascript.js')) +runJob(join(__dirname, './script.css.js')) diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.app-ext.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.app-ext.js new file mode 100644 index 00000000000..8c687c33f69 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.app-ext.js @@ -0,0 +1,60 @@ +const + fs = require('fs'), + path = require('path'), + root = path.resolve(__dirname, '../..'), + resolvePath = file => path.resolve(root, file), + { blue } = require('chalk') + +const writeJson = function (file, json) { + return fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n', 'utf-8') +} + +module.exports.syncAppExt = function (both = true) { + // make sure this project has an app-extension project + const appExtDir = resolvePath('app-extension') + if (!fs.existsSync(appExtDir)) { + return + } + + // make sure this project has an ui project + const uiDir = resolvePath('ui') + if (!fs.existsSync(uiDir)) { + return + } + + // get version and name from ui package.json + const { name, version } = require(resolvePath(resolvePath('ui/package.json'))) + + // read app-ext package.json + const appExtFile = resolvePath('app-extension/package.json') + let appExtJson = require(appExtFile), + finished = false + + // sync version numbers + if (both === true) { + appExtJson.version = version + } + + // check dependencies + if (appExtJson.dependencies !== void 0) { + if (appExtJson.dependencies[name] !== void 0) { + appExtJson.dependencies[name] = '^' + version + finished = true + } + } + // check devDependencies, if not finished + if (finished === false && appExtJson.devDependencies !== void 0) { + if (appExtJson.devDependencies[name] !== void 0) { + appExtJson.devDependencies[name] = '^' + version + finished = true + } + } + + if (finished === true) { + writeJson(appExtFile, appExtJson) + console.log(` ⭐️ App Extension version ${blue(appExtJson.name)} synced with UI version.\n`) + return + } + + console.error(' App Extension version and dependency NOT synced.\n') +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.clean.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.clean.js new file mode 100644 index 00000000000..e700bb7d53c --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.clean.js @@ -0,0 +1,6 @@ +var + rimraf = require('rimraf'), + path = require('path') + +rimraf.sync(path.resolve(__dirname, '../dist/*')) +console.log(' 💥 Cleaned build artifacts.\n') diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.css.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.css.js new file mode 100644 index 00000000000..99cbfcc9242 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.css.js @@ -0,0 +1,75 @@ +const path = require('path') +const sass = require('sass') +const postcss = require('postcss') +const cssnano = require('cssnano') +const rtl = require('rtlcss') +const autoprefixer = require('autoprefixer') + +const buildConf = require('./config') +const buildUtils = require('./utils') + +const postCssCompiler = postcss([ autoprefixer ]) +const postCssRtlCompiler = postcss([ rtl({}) ]) + +const nano = postcss([ + cssnano({ + preset: ['default', { + mergeLonghand: false, + convertValues: false, + cssDeclarationSorter: false, + reduceTransforms: false + }] + }) +]) + +Promise + .all([ + generate('src/index.sass', `dist/index`) + ]) + .catch(e => { + console.error(e) + process.exit(1) + }) + +/** + * Helpers + */ + +function resolve (_path) { + return path.resolve(__dirname, '..', _path) +} + +function generate (src, dest) { + src = resolve(src) + dest = resolve(dest) + + return new Promise((resolve, reject) => { + sass.render({ file: src, includePaths: ['node_modules'] }, (err, result) => { + if (err) { + reject(err) + return + } + + resolve(result.css) + }) + }) + .then(code => buildConf.banner + code) + .then(code => postCssCompiler.process(code, { from: void 0 })) + .then(code => { + code.warnings().forEach(warn => { + console.warn(warn.toString()) + }) + return code.css + }) + .then(code => Promise.all([ + generateUMD(dest, code), + postCssRtlCompiler.process(code, { from: void 0 }) + .then(code => generateUMD(dest, code.css, '.rtl')) + ])) +} + +function generateUMD (dest, code, ext = '') { + return buildUtils.writeFile(`${dest}${ext}.css`, code, true) + .then(code => nano.process(code, { from: void 0 })) + .then(code => buildUtils.writeFile(`${dest}${ext}.min.css`, code.css, true)) +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.javascript.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.javascript.js new file mode 100644 index 00000000000..4c8565ed79a --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.javascript.js @@ -0,0 +1,204 @@ +const path = require('path') +const fs = require('fs') +const fse = require('fs-extra') +const rollup = require('rollup') +const uglify = require('uglify-es') +const buble = require('@rollup/plugin-buble') +const json = require('@rollup/plugin-json') +const { nodeResolve } = require('@rollup/plugin-node-resolve') + +const buildConf = require('./config') +const buildUtils = require('./utils') + +const rollupPlugins = [ + nodeResolve({ + extensions: ['.js'], + preferBuiltins: false + }), + json(), + buble({ + objectAssign: 'Object.assign' + }) +] + +const builds = [ + { + rollup: { + input: { + input: pathResolve('entry/index.esm.js') + }, + output: { + file: pathResolve('../dist/index.esm.js'), + format: 'es' + } + }, + build: { + // unminified: true, + minified: true + } + }, + { + rollup: { + input: { + input: pathResolve('entry/index.common.js') + }, + output: { + file: pathResolve('../dist/index.common.js'), + format: 'cjs' + } + }, + build: { + // unminified: true, + minified: true + } + }, + { + rollup: { + input: { + input: pathResolve('entry/index.umd.js') + }, + output: { + name: '<%= umdExportName %>', + file: pathResolve('../dist/index.umd.js'), + format: 'umd' + } + }, + build: { + unminified: true, + minified: true, + minExt: true + } + } +] + +// Add your asset folders here, if needed +// addAssets(builds, 'icon-set', 'iconSet') +// addAssets(builds, 'lang', 'lang') + +build(builds) + +/** + * Helpers + */ + +function pathResolve (_path) { + return path.resolve(__dirname, _path) +} + +// eslint-disable-next-line no-unused-vars +function addAssets (builds, type, injectName) { + const + files = fs.readdirSync(pathResolve('../../ui/src/components/' + type)), + plugins = [ buble(/* bubleConfig */) ], + outputDir = pathResolve(`../dist/${type}`) + + fse.mkdirp(outputDir) + + files + .filter(file => file.endsWith('.js')) + .forEach(file => { + const name = file.substr(0, file.length - 3).replace(/-([a-z])/g, g => g[1].toUpperCase()) + builds.push({ + rollup: { + input: { + input: pathResolve(`../src/components/${type}/${file}`), + plugins + }, + output: { + file: addExtension(pathResolve(`../dist/${type}/${file}`), 'umd'), + format: 'umd', + name: `<%= umdExportName %>.${injectName}.${name}` + } + }, + build: { + minified: true + } + }) + }) +} + +function build (builds) { + return Promise + .all(builds.map(genConfig).map(buildEntry)) + .catch(buildUtils.logError) +} + +function genConfig (opts) { + Object.assign(opts.rollup.input, { + plugins: rollupPlugins, + external: [ 'vue', 'quasar' ] + }) + + Object.assign(opts.rollup.output, { + banner: buildConf.banner, + globals: { vue: 'Vue', quasar: 'Quasar' } + }) + + return opts +} + +function addExtension (filename, ext = 'min') { + const insertionPoint = filename.lastIndexOf('.') + return `${filename.slice(0, insertionPoint)}.${ext}${filename.slice(insertionPoint)}` +} + +function buildEntry (config) { + return rollup + .rollup(config.rollup.input) + .then(bundle => bundle.generate(config.rollup.output)) + .then(({ output }) => { + const code = config.rollup.output.format === 'umd' + ? injectVueRequirement(output[0].code) + : output[0].code + + return config.build.unminified + ? buildUtils.writeFile(config.rollup.output.file, code) + : code + }) + .then(code => { + if (!config.build.minified) { + return code + } + + const minified = uglify.minify(code, { + compress: { + pure_funcs: ['makeMap'] + } + }) + + if (minified.error) { + return Promise.reject(minified.error) + } + + return buildUtils.writeFile( + config.build.minExt === true + ? addExtension(config.rollup.output.file) + : config.rollup.output.file, + buildConf.banner + minified.code, + true + ) + }) + .catch(err => { + console.error(err) + process.exit(1) + }) +} + +function injectVueRequirement (code) { + // eslint-disable-next-line + const index = code.indexOf(`Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue`) + + if (index === -1) { + return code + } + + const checkMe = ` if (Vue === void 0) { + console.error('[ Quasar ] Vue is required to run. Please add a script tag for it before loading Quasar.') + return + } + ` + + return code.substring(0, index - 1) + + checkMe + + code.substring(index) +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.open-umd.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.open-umd.js new file mode 100644 index 00000000000..71d90858984 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/script.open-umd.js @@ -0,0 +1,6 @@ +const { resolve } = require('path') +const open = require('open') + +open( + resolve(__dirname, '../umd-test.html') +) diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/utils.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/utils.js new file mode 100644 index 00000000000..9d6f302426c --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/build/utils.js @@ -0,0 +1,55 @@ +const + fs = require('fs'), + path = require('path'), + zlib = require('zlib'), + { green, blue, red, cyan } = require('chalk') + +function getSize (code) { + return (code.length / 1024).toFixed(2) + 'kb' +} + +module.exports.createFolder = function (folder) { + const dir = path.join(__dirname, '..', folder) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir) + } +} + +module.exports.writeFile = function (dest, code, zip) { + const banner = dest.indexOf('.json') > -1 + ? red('[json]') + : dest.indexOf('.js') > -1 + ? green('[js] ') + : dest.indexOf('.ts') > -1 + ? cyan('[ts] ') + : blue('[css] ') + + return new Promise((resolve, reject) => { + function report (extra) { + console.log(`${banner} ${path.relative(process.cwd(), dest).padEnd(41)} ${getSize(code).padStart(8)}${extra || ''}`) + resolve(code) + } + + fs.writeFile(dest, code, err => { + if (err) return reject(err) + if (zip) { + zlib.gzip(code, (err, zipped) => { + if (err) return reject(err) + report(` (gzipped: ${getSize(zipped).padStart(8)})`) + }) + } + else { + report() + } + }) + }) +} + +module.exports.readFile = function (file) { + return fs.readFileSync(file, 'utf-8') +} + +module.exports.logError = function (err) { + console.error('\n' + red('[Error]'), err) + console.log() +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/README.md b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/README.md new file mode 100644 index 00000000000..fbe4ca39e71 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/README.md @@ -0,0 +1,3 @@ +# Dev app (playground) + +Adding .vue files to src/pages/ will auto-add them to the Index page list. diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.editorconfig b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.editorconfig new file mode 100644 index 00000000000..9d08a1a828a --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.gitignore b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.gitignore new file mode 100644 index 00000000000..d7c7a2d6336 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.gitignore @@ -0,0 +1,20 @@ +.quasar +.DS_Store +.thumbs.db +node_modules +/dist +/src-cordova/node_modules +/src-cordova/platforms +/src-cordova/plugins +/src-cordova/www +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.postcssrc.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.postcssrc.js new file mode 100644 index 00000000000..1174fe52b74 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // to edit target browsers: use "browserslist" field in package.json + require('autoprefixer') + ] +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_package.json b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_package.json new file mode 100644 index 00000000000..e0fa645b4a5 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/_package.json @@ -0,0 +1,37 @@ +{ + "name": "dev", + "version": "1.0.0", + "description": "A Quasar Framework app", + "productName": "Quasar App", + "private": true, + "scripts": { + "dev": "quasar dev", + "dev:ssr": "quasar dev -m ssr", + "dev:ios": "quasar dev -m ios", + "dev:android": "quasar dev -m android", + "dev:electron": "quasar dev -m electron" + }, + "dependencies": { + "@quasar/extras": "^1.16.4", + "core-js": "^3.0.0" + }, + "devDependencies": { + "@quasar/app": "^2.0.0" + }, + "browserslist": [ + "last 10 Chrome versions", + "last 10 Firefox versions", + "last 4 Edge versions", + "last 7 Safari versions", + "last 8 Android versions", + "last 8 ChromeAndroid versions", + "last 8 FirefoxAndroid versions", + "last 10 iOS versions", + "last 5 Opera versions" + ], + "engines": { + "node": ">= 8.9.0", + "npm": ">= 5.6.0", + "yarn": ">= 1.6.0" + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/babel.config.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/babel.config.js new file mode 100644 index 00000000000..9408c6cd4aa --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@quasar/babel-preset-app' + ] +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/quasar.conf.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/quasar.conf.js new file mode 100644 index 00000000000..fd83be248af --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/quasar.conf.js @@ -0,0 +1,60 @@ +// Configuration for your app +// https://quasar.dev/quasar-cli/quasar-conf-js + +const path = require('path') + +module.exports = function (ctx) { + return { + // app boot file (/src/boot) + // --> boot files are part of "main.js" + boot: [ + 'register.js' + ], + + css: [ + 'app.sass' + ], + + extras: [ + // 'ionicons-v4', + // 'mdi-v5', + // 'fontawesome-v5', + // 'eva-icons', + // 'themify', + // 'line-awesome', + // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! + + 'roboto-font', // optional, you are not bound to it + 'material-icons' // optional, you are not bound to it + ], + + framework: { + // iconSet: 'material-icons', // Quasar icon set + // lang: 'en-US', // Quasar language pack + + config: {}, + + // Quasar plugins + plugins: [] + }, + + // animations: 'all', // --- includes all animations + animations: [], + + // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build + build: { + vueRouterMode: 'history', + + chainWebpack (chain) { + chain.resolve.alias.merge({ + ui: path.resolve(__dirname, '../src/index.js') + }) + } + }, + + devServer: { + // port: 8080, + open: true // opens browser window automatically + } + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/App.vue b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/App.vue new file mode 100644 index 00000000000..cb59d2e8377 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/assets/_.gitkeep b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/assets/_.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/boot/register.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/boot/register.js new file mode 100644 index 00000000000..bf808877d0b --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/boot/register.js @@ -0,0 +1,4 @@ +import Vue from 'vue' +import VuePlugin from 'ui' // "ui" is aliased in quasar.conf.js + +Vue.use(VuePlugin) diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/components/_.gitkeep b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/components/_.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/css/app.sass b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/css/app.sass new file mode 100644 index 00000000000..5faa1cef3ae --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/css/app.sass @@ -0,0 +1,2 @@ +// app global css in Sass form +@import '../../../src/index.sass' diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/css/quasar.variables.sass b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/css/quasar.variables.sass new file mode 100644 index 00000000000..66d496562eb --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/css/quasar.variables.sass @@ -0,0 +1,22 @@ +// Quasar Sass (& SCSS) Variables +// -------------------------------------------------- +// To customize the look and feel of this app, you can override +// the Stylus variables found in Quasar's source Stylus files. + +// Check documentation for full list of Quasar variables + +// Your own variables (that are declared here) and Quasar's own +// ones will be available out of the box in your .vue/.scss/.sass files + +// It's highly recommended to change the default colors +// to match your app's branding. +// Tip: Use the "Theme Builder" on Quasar's documentation website. + +$primary : #027BE3 +$secondary : #26A69A +$accent : #9C27B0 + +$positive : #21BA45 +$negative : #C10015 +$info : #31CCEC +$warning : #F2C037 diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/index.template.html b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/index.template.html new file mode 100644 index 00000000000..ab22e862951 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/index.template.html @@ -0,0 +1,16 @@ + + + + <%= '\<%= productName %\>' %> + + + + + + + + + +
+ + diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/layouts/MyLayout.vue b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/layouts/MyLayout.vue new file mode 100644 index 00000000000..bb3b13da5c2 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/layouts/MyLayout.vue @@ -0,0 +1,39 @@ + + + diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/pages/Index.vue b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/pages/Index.vue new file mode 100644 index 00000000000..a2508a09bc3 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/pages/Index.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/pages/Test1.vue b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/pages/Test1.vue new file mode 100644 index 00000000000..7259a522720 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/pages/Test1.vue @@ -0,0 +1,18 @@ + + + diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/index.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/index.js new file mode 100644 index 00000000000..8a42f5016c9 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/index.js @@ -0,0 +1,43 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' + +import routes from './routes' + +Vue.use(VueRouter) + +/* + * If not building with SSR mode, you can + * directly export the Router instantiation + */ + +export default function (/* { store, ssrContext } */) { + const Router = new VueRouter({ + scrollBehavior: () => ({ x: 0, y: 0 }), + routes, + + // Leave these as is and change from quasar.conf.js instead! + // quasar.conf.js -> build -> vueRouterMode + // quasar.conf.js -> build -> publicPath + mode: process.env.VUE_ROUTER_MODE, + base: process.env.VUE_ROUTER_BASE + }) + + // we get each page from server first! + if (process.env.MODE === 'ssr' && process.env.CLIENT) { + console.log('!!!!') + console.log('On route change we deliberately load page from server -- in order to test hydration errors') + console.log('!!!!') + + let reload = false + Router.beforeEach((to, _, next) => { + if (reload) { + window.location.href = to.fullPath + return + } + reload = true + next() + }) + } + + return Router +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/pages.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/pages.js new file mode 100644 index 00000000000..7a01334469f --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/pages.js @@ -0,0 +1,27 @@ +/* + * Export files list for /pages folder + */ + +function kebabCase (str) { + const result = str.replace( + /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g, + match => '-' + match.toLowerCase() + ) + return (str[0] === str[0].toUpperCase()) + ? result.substring(1) + : result +} + +function slugify (str) { + return encodeURIComponent(String(str).trim().replace(/\s+/g, '-')) +} + +export default require.context('../pages', true, /^\.\/.*\.vue$/) + .keys() + .map(page => page.slice(2).replace('.vue', '')) + .filter(page => page !== 'Index') + .map(page => ({ + file: page, + title: page + '.vue', + path: slugify(kebabCase(page)) + })) diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/routes.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/routes.js new file mode 100644 index 00000000000..6b9889cade2 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/dev/src/router/routes.js @@ -0,0 +1,18 @@ +import pages from './pages' + +const children = pages.map(page => ({ + path: page.path, + component: () => import('pages/' + page.file + '.vue') +})) + +const routes = [ + { + path: '/', + component: () => import('layouts/MyLayout.vue'), + children: [ + { path: '', component: () => import('pages/Index.vue') } + ].concat(children) + } +] + +export default routes diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/index.js b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/index.js new file mode 100644 index 00000000000..e06b09007bc --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/index.js @@ -0,0 +1,23 @@ +import { version } from '../package.json' + +<% if (features.component) { %>import Component from './components/Component'<% } %> +<% if (features.directive) { %>import Directive from './directives/Directive'<% } %> + +export { + version, + + <% if (features.component) { %>Component<% if (features.directive) { %>,<% } } %> + <% if (features.directive) { %>Directive<% } %> +} + +export default { + version, + + <% if (features.component) { %>Component,<% } %> + <% if (features.directive) { %>Directive,<% } %> + + install (Vue) { + <% if (features.component) { %>Vue.component(Component.name, Component)<% } %> + <% if (features.directive) { %>Vue.directive(Directive.name, Directive)<% } %> + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/index.sass b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/index.sass new file mode 100644 index 00000000000..6b331e43aea --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/index.sass @@ -0,0 +1,3 @@ +@import 'quasar/src/css/variables.sass' +@import './components/Component.sass' +@import './directives/Directive.sass' diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/mixins/_.gitkeep b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/src/mixins/_.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/umd-test.html b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/umd-test.html new file mode 100644 index 00000000000..cb8a98b08ec --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/BASE/ui/umd-test.html @@ -0,0 +1,58 @@ + + + + + + + + + + UMD test + + + + + +
+ + + + + quasar-ui-<%= name %> v{{ version }} + + +
Quasar v{{ $q.version }}
+
+
+ + + +
    +
  • In /ui, run: "yarn build"
  • +
  • You need to build & refresh page on each change manually.
  • +
  • Use self-closing tags only!
  • +
  • Example: <my-component></my-component>
  • +
+
+
+
+
+ + + + + + + + + diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae-install/app-extension/src/install.js b/create-quasar/templates/ui-kit/quasar-v1/ae-install/app-extension/src/install.js new file mode 100644 index 00000000000..ca063af2614 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae-install/app-extension/src/install.js @@ -0,0 +1,9 @@ +/** + * Quasar App Extension install script + * + * Docs: https://quasar.dev/app-extensions/development-guide/install-api + */ + +module.exports = function (api) { + // +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae-prompts/app-extension/src/prompts.js b/create-quasar/templates/ui-kit/quasar-v1/ae-prompts/app-extension/src/prompts.js new file mode 100644 index 00000000000..8382318a612 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae-prompts/app-extension/src/prompts.js @@ -0,0 +1,44 @@ +/** + * Quasar App Extension prompts script + * + * Docs: https://quasar.dev/app-extensions/development-guide/prompts-api + * + * Inquirer prompts + * (answers are available as "api.prompts" in the other scripts) + * https://www.npmjs.com/package/inquirer#question + * + * Example: + + return [ + { + name: 'name', + type: 'string', + required: true, + message: 'Quasar CLI Extension name (without prefix)', + }, + { + name: 'preset', + type: 'checkbox', + message: 'Check the features needed for your project:', + choices: [ + { + name: 'Install script', + value: 'install' + }, + { + name: 'Prompts script', + value: 'prompts' + }, + { + name: 'Uninstall script', + value: 'uninstall' + } + ] + } + ] + + */ + +module.exports = function () { + return [] +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae-uninstall/app-extension/src/uninstall.js b/create-quasar/templates/ui-kit/quasar-v1/ae-uninstall/app-extension/src/uninstall.js new file mode 100644 index 00000000000..f9d212585d6 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae-uninstall/app-extension/src/uninstall.js @@ -0,0 +1,9 @@ +/** + * Quasar App Extension uninstall script + * + * Docs: https://quasar.dev/app-extensions/development-guide/uninstall-api + */ + +module.exports = function (api) { + // +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/README.md b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/README.md new file mode 100644 index 00000000000..384ba279d4c --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/README.md @@ -0,0 +1,33 @@ +# Quasar App Extension <%= name %> + +> Add a short description of your App Extension. What does it do? How is it beneficial? Why would someone want to use it? + +[![npm](https://img.shields.io/npm/v/quasar-app-extension-<%= name %>.svg?label=quasar-app-extension-<%= name %>)](https://www.npmjs.com/package/quasar-app-extension-<%= name %>) +[![npm](https://img.shields.io/npm/dt/quasar-app-extension-<%= name %>.svg)](https://www.npmjs.com/package/quasar-app-extension-<%= name %>) + +# Install +```bash +quasar ext add <%= name %> +``` +Quasar CLI will retrieve it from NPM and install the extension. + +## Prompts + +> If your app extension uses prompts, explain them here, otherwise remove this section and remove prompts.js file. + +# Uninstall +```bash +quasar ext remove <%= name %> +``` + +# Info +> Add longer information here that will help the user of your app extension. + +# Other Info +> Add other information that's not as important to know + +# Donate +If you appreciate the work that went into this App Extension, please consider [donating to Quasar](https://donate.quasar.dev). + +# License +<%= license %> (c) <%= author %> diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/_.npmignore b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/_.npmignore new file mode 100644 index 00000000000..b4084a38f49 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/_.npmignore @@ -0,0 +1,17 @@ +.DS_Store +.thumbs.db +yarn.lock +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +.editorconfig +.eslintignore +.eslintrc.js diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/_package.json b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/_package.json new file mode 100644 index 00000000000..b2517f9e145 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/_package.json @@ -0,0 +1,16 @@ +{ + "name": "quasar-app-extension-<%= name %>", + "version": "0.0.1", + "description": "<%= aeDescription %>", + "author": "<%= author %>", + "license": "<%= license %>", + "main": "src/index.js", + "dependencies": { + "quasar-ui-<%= name %>": "latest" + }, + "engines": { + "node": ">= 8.9.0", + "npm": ">= 5.6.0", + "yarn": ">= 1.6.0" + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/boot/register.js b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/boot/register.js new file mode 100644 index 00000000000..fe2c4c3ec86 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/boot/register.js @@ -0,0 +1,4 @@ +import Vue from 'vue' +import VuePlugin from 'quasar-ui-<%= name %>' + +Vue.use(VuePlugin) diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/index.js b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/index.js new file mode 100644 index 00000000000..8ffaca468ac --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/index.js @@ -0,0 +1,36 @@ +/** + * Quasar App Extension index/runner script + * (runs on each dev/build) + * + * Docs: https://quasar.dev/app-extensions/development-guide/index-api + */ + +function extendConf (conf) { + // register our boot file + conf.boot.push('~quasar-app-extension-<%= name %>/src/boot/register.js') + + // make sure app extension files & ui package gets transpiled + conf.build.transpileDependencies.push(/quasar-app-extension-<%= name %>[\\/]src/) + + // make sure the stylesheet goes through webpack to avoid SSR issues + conf.css.push('~quasar-ui-<%= name %>/src/index.sass') +} + +module.exports = function (api) { + // Quasar compatibility check; you may need + // hard dependencies, as in a minimum version of the "quasar" + // package or a minimum version of "@quasar/app" CLI + api.compatibleWith('quasar', '^1.1.1') + api.compatibleWith('@quasar/app', '^2.0.0') + +<% if (features.component) { %> + // Uncomment the line below if you provide a JSON API for your component + // api.registerDescribeApi('<%= componentName %>', '~quasar-ui-<%= name %>/src/components/<%= componentName %>.json') +<% } %><% if (features.directive) { %> + // Uncomment the line below if you provide a JSON API for your directive + // api.registerDescribeApi('<%= directiveName %>', '~quasar-ui-<%= name %>/src/directives/<%= directiveName %>.json') +<% } %> + + // We extend /quasar.conf.js + api.extendQuasarConf(extendConf) +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/templates/_.gitkeep b/create-quasar/templates/ui-kit/quasar-v1/ae/app-extension/src/templates/_.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/create-quasar/templates/ui-kit/quasar-v1/index.js b/create-quasar/templates/ui-kit/quasar-v1/index.js new file mode 100644 index 00000000000..91fbe380adb --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/index.js @@ -0,0 +1,18 @@ +export async function script ({ scope, utils }) { + utils.createTargetDir(scope) + utils.renderTemplate('BASE', scope) + + if (scope.features.ae) { + utils.renderTemplate('ae', scope) + if (scope.preset.install) utils.renderTemplate('ae-install', scope) + if (scope.preset.prompts) utils.renderTemplate('ae-prompts', scope) + if (scope.preset.uninstall) utils.renderTemplate('ae-uninstall', scope) + } + + if (scope.features.component) utils.renderTemplate('ui-component', scope) + if (scope.features.directive) utils.renderTemplate('ui-directive', scope) + + if (scope.features.ae && (scope.features.component || scope.features.directive)) { + utils.renderTemplate('ui-ae', scope) + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ui-ae/ui/build/script.app-ext.js b/create-quasar/templates/ui-kit/quasar-v1/ui-ae/ui/build/script.app-ext.js new file mode 100644 index 00000000000..29e1be07154 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ui-ae/ui/build/script.app-ext.js @@ -0,0 +1,60 @@ +const + fs = require('fs'), + path = require('path'), + root = path.resolve(__dirname, '../..'), + resolvePath = file => path.resolve(root, file), + { blue } = require('chalk') + +const writeJson = function (file, json) { + return fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n', 'utf-8') +} + +module.exports.syncAppExt = function (both = true) { + // make sure this project has an app-extension project + const appExtDir = resolvePath('app-extension') + if (!fs.existsSync(appExtDir)) { + return + } + + // make sure this project has an ui project + const uiDir = resolvePath('ui') + if (!fs.existsSync(uiDir)) { + return + } + + // get version and name from ui package.json + const { name, version } = require(resolvePath(resolvePath('ui/package.json'))) + + // read app-ext package.json + const appExtFile = resolvePath('app-extension/package.json') + let appExtJson = require(appExtFile), + finished = false + + // sync version numbers + if (both === true) { + appExtJson.version = version + } + + // check dependencies + if (appExtJson.dependencies !== void 0) { + if (appExtJson.dependencies[name] !== void 0) { + appExtJson.dependencies[name] = '^' + version + finished = true + } + } + // check devDependencies, if not finished + if (finished === false && appExtJson.devDependencies !== void 0) { + if (appExtJson.devDependencies[name] !== void 0) { + appExtJson.devDependencies[name] = '^' + version + finished = true + } + } + + if (finished === true) { + writeJson(appExtFile, appExtJson) + console.log(` ⭐️ App Extension version ${blue(appExtJson.name)} synced with UI version.\n`) + return + } + + console.error(` App Extension version and dependency NOT synced.\n`) +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ui-component/ui/src/components/Component.js b/create-quasar/templates/ui-kit/quasar-v1/ui-component/ui/src/components/Component.js new file mode 100644 index 00000000000..d412daf4c6a --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ui-component/ui/src/components/Component.js @@ -0,0 +1,14 @@ +import { QBadge } from 'quasar' + +export default { + name: '<%= componentName %>', + + render (h) { + return h(QBadge, { + staticClass: '<%= componentName %>', + props: { + label: '<%= componentName %>' + } + }) + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ui-component/ui/src/components/Component.sass b/create-quasar/templates/ui-kit/quasar-v1/ui-component/ui/src/components/Component.sass new file mode 100644 index 00000000000..47398cb7b48 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ui-component/ui/src/components/Component.sass @@ -0,0 +1,2 @@ +.<%= componentName %> + font-weight: bold diff --git a/create-quasar/templates/ui-kit/quasar-v1/ui-directive/ui/src/directives/Directive.js b/create-quasar/templates/ui-kit/quasar-v1/ui-directive/ui/src/directives/Directive.js new file mode 100644 index 00000000000..03119cdc07a --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ui-directive/ui/src/directives/Directive.js @@ -0,0 +1,7 @@ +export default { + name: '<%= directiveName %>', + + bind (el) { + // + } +} diff --git a/create-quasar/templates/ui-kit/quasar-v1/ui-directive/ui/src/directives/Directive.sass b/create-quasar/templates/ui-kit/quasar-v1/ui-directive/ui/src/directives/Directive.sass new file mode 100644 index 00000000000..2f1635cbaf2 --- /dev/null +++ b/create-quasar/templates/ui-kit/quasar-v1/ui-directive/ui/src/directives/Directive.sass @@ -0,0 +1,2 @@ +.<%= directiveName %> + font-size: 14px diff --git a/create-quasar/utils/index.js b/create-quasar/utils/index.js new file mode 100644 index 00000000000..f75c74d3ad4 --- /dev/null +++ b/create-quasar/utils/index.js @@ -0,0 +1,384 @@ +import { readFileSync, writeFileSync, existsSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { sep, dirname, normalize, join, resolve, extname } from 'node:path' +import { spawn, execSync as exec } from 'node:child_process' + +import { emptyDirSync, ensureDirSync, ensureFileSync, copySync } from 'fs-extra/esm' +import promptUser from 'prompts' +import compileTemplate from 'lodash/template.js' +import { globSync } from 'tinyglobby' +import { yellow, green } from 'kolorist' + +import logger from './logger.js' + +const TEMPLATING_FILE_EXTENSIONS = [ '', '.json', '.js', '.cjs', '.ts', '.vue', '.md', '.html', '.sass' ] + +async function prompts (scope, questions, opts) { + const options = opts || { + onCancel: () => { + logger.fatal('Scaffolding cancelled') + } + } + + const answers = await promptUser(questions, options) + Object.assign(scope, answers) +} + +function createTargetDir (scope) { + console.log() + logger.log('Generating files...') + console.log() + + const fn = scope.overwrite ? emptyDirSync : ensureDirSync + fn(scope.projectFolder) +} + +function convertArrayToObject (arr) { + const acc = {} + arr.forEach(key => { + acc[ key ] = true + }) + return acc +} + +const runningPackageManager = (() => { + const userAgent = process.env.npm_config_user_agent + if (!userAgent) { + return + } + + const [ name, version ] = userAgent.split(' ')[ 0 ].split('/') + return { name, version } +})() + +function getCallerPath () { + const _prepareStackTrace = Error.prepareStackTrace + Error.prepareStackTrace = (_, stack) => stack + const stack = new Error().stack.slice(1) + Error.prepareStackTrace = _prepareStackTrace + const filename = stack[ 1 ].getFileName() + return dirname( + filename.startsWith('file://') + ? fileURLToPath(filename) + : filename + ) +} + +function renderTemplate (relativePath, scope) { + const templateDir = join(getCallerPath(), relativePath) + const files = globSync([ '**/*' ], { cwd: templateDir }) + + for (const rawPath of files) { + const targetRelativePath = rawPath.split('/').map(name => { + // dotfiles are ignored when published to npm, therefore in templates + // we need to prefix them with an underscore (e.g. "_.gitignore") + // Also, some tools like ESLint expect valid config files, therefore + // we also prefix files like "package.json" too. (e.g. "_package.json") + return name.startsWith('_') + ? name.slice(1) + : name + }).join('/') + + const targetPath = resolve(scope.projectFolder, targetRelativePath) + const sourcePath = resolve(templateDir, rawPath) + const extension = extname(targetRelativePath) + + ensureFileSync(targetPath) + + console.log(` ${ green('-') } ${ targetRelativePath }`) + + if (TEMPLATING_FILE_EXTENSIONS.includes(extension)) { + const rawContent = readFileSync(sourcePath, 'utf-8') + const template = compileTemplate(rawContent, { interpolate: /<%=([\s\S]+?)%>/g }) + + let newContent = template(scope) + if (extension === '.json') { + try { + // try to format the JSON + newContent = JSON.stringify(JSON.parse(newContent), null, 2) + } catch { + // noop, the JSON might be containing comments, leave it unformatted + } + } + + writeFileSync(targetPath, newContent, 'utf-8') + } + else { + copySync(sourcePath, targetPath) + } + } +} + +function isValidPackageName (projectName) { + return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test( + projectName + ) +} + +function inferPackageName (projectFolder) { + return projectFolder + .trim() + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/^[._]/, '') + .replace(/[^a-z0-9-~]+/g, '-') +} + +function escapeString (val) { + return JSON.stringify(val).slice(1, -1) +} + +function getGitUser () { + let name + let email + + try { + name = exec('git config --get user.name') + email = exec('git config --get user.email') + } + catch (e) {} + + name = name && JSON.stringify(name.toString().trim()).slice(1, -1) + email = email && (' <' + email.toString().trim() + '>') + + return (name || '') + (email || '') +} + +/** + * Prints the final message with instructions of necessary next steps. + * + * @param {Object} scope Data from questionnaire. + */ +function printFinalMessage (scope) { + const verPrefix = scope.quasarVersion ? scope.quasarVersion + '.' : '' + const message = ` + To get started: + ${ yellow(` + cd ${ scope.projectFolderName }${ scope.skipDepsInstall !== true && scope.packageManager === false ? ` + yarn #or: npm install + yarn lint --fix # or: npm run lint -- --fix` : '' }${ scope.skipDepsInstall !== true ? ` + quasar dev # or: yarn quasar dev # or: npx quasar dev` : '' } + `) } + Documentation can be found at: https://${ verPrefix }quasar.dev + + Quasar is relying on donations to evolve. We'd be very grateful if you can + read our manifest on "Why donations are important": https://${ verPrefix }quasar.dev/why-donate + Donation campaign: https://donate.quasar.dev + Any amount is very welcome. + If invoices are required, please first contact Razvan Stoenescu. + + Please give us a star on Github if you appreciate our work: + https://github.com/quasarframework/quasar + + Enjoy! - Quasar Team +` + + console.log(message) +} + +function runCommand (cmd, args, options) { + console.log() + return new Promise((resolve, reject) => { + const runner = spawn( + cmd, + args, + Object.assign({ + cwd: process.cwd(), + stdio: 'inherit', + shell: true + }, options) + ) + + runner.on('exit', code => { + console.log() + + if (code) { + console.log(` ${ cmd } FAILED...`) + console.log() + reject() + } + else { + resolve() + } + }) + }) +} + +function installDeps (scope) { + const args = [ 'install' ] + // Related to scripts/create-test-project.ts + if (process.env.CREATE_TEST_PROJECT_OVERRIDE === 'true') { + // If we don't use this flag, then the test project will become part of the monorepo and fail to install properly + args.push('--ignore-workspace') + } + + return runCommand( + scope.packageManager, + args, + { cwd: scope.projectFolder } + ) +} + +function lintFolder (scope) { + return runCommand( + scope.packageManager, + scope.packageManager === 'npm' + ? [ 'run', 'lint', '--', '--fix' ] + : [ 'run', 'lint', '--fix' ], + { cwd: scope.projectFolder } + ) +} + +function hasGit () { + try { + exec('git --version') + return true + } + catch (_) {} +} + +function folderHasGit (cwd) { + try { + exec('git status', { stdio: 'ignore', cwd }) + return true + } + catch (_) {} +} + +function initializeGit (projectFolder) { + if (hasGit() !== true) { + logger.log('Git is not installed on the system, so skipping Git repo initialization.') + return + } + + if (folderHasGit(projectFolder) === true) { + logger.log('A parent of the project folder is already a Git repository, so skipping Git initialization.') + return + } + + try { + exec('git init', { cwd: projectFolder }) + exec('git add -A', { cwd: projectFolder }) + exec('git commit -m "Initialize the project 🚀" --no-verify', { cwd: projectFolder }) + } + catch (e) { + logger.warn('Could not initialize Git repository. Please do this manually.') + return + } + + logger.log('Initialized Git repository 🚀') +} + +const quasarConfigFilenameList = [ + 'quasar.config.js', + 'quasar.config.mjs', + 'quasar.config.ts', + 'quasar.config.cjs', + 'quasar.conf.js' // legacy +] + +function ensureOutsideProject () { + let dir = process.cwd() + + while (dir.length && dir[ dir.length - 1 ] !== sep) { + for (const name of quasarConfigFilenameList) { + const filename = join(dir, name) + if (existsSync(filename)) { + logger.fatal('Error. This command must NOT be executed inside of a Quasar project folder.') + } + } + + dir = normalize(join(dir, '..')) + } +} + +const QUASAR_VERSIONS = [ + { title: 'Quasar v2 (Vue 3 | latest and greatest)', value: 'v2', description: 'recommended' }, + { title: 'Quasar v1 (Vue 2)', value: 'v1' } +] +const SCRIPT_TYPES = [ + { title: 'Javascript', value: 'js' }, + { title: 'Typescript', value: 'ts' } +] + +const commonPrompts = { + quasarVersion: { + type: 'select', + name: 'quasarVersion', + message: 'Pick Quasar version:', + initial: 0, + choices: QUASAR_VERSIONS + }, + + scriptType: { + type: 'select', + name: 'scriptType', + message: 'Pick script type:', + initial: 0, + choices: SCRIPT_TYPES + }, + + productName: { + type: 'text', + name: 'productName', + message: 'Project product name: (must start with letter if building mobile apps)', + initial: 'Quasar App', + validate: val => + (val && val.length > 0) || 'Invalid product name' + }, + + description: { + type: 'text', + name: 'description', + message: 'Project description:', + initial: 'A Quasar Project', + format: escapeString, + validate: val => + val.length > 0 || 'Invalid project description' + }, + + license: { + type: 'text', + name: 'license', + message: 'License type', + initial: 'MIT' + } +} + +export async function injectAuthor (scope) { + const author = getGitUser() + + if (author) { + scope.author = author + return + } + + await prompts(scope, [ + { + type: 'text', + name: 'author', + message: 'Author:' + } + ]) +} + +export default { + logger, + + prompts, + createTargetDir, + convertArrayToObject, + runningPackageManager, + renderTemplate, + isValidPackageName, + inferPackageName, + + printFinalMessage, + installDeps, + lintFolder, + ensureOutsideProject, + initializeGit, + + commonPrompts, + injectAuthor +} diff --git a/create-quasar/utils/logger.js b/create-quasar/utils/logger.js new file mode 100644 index 00000000000..5cfff7dc039 --- /dev/null +++ b/create-quasar/utils/logger.js @@ -0,0 +1,157 @@ +import { + bgGreen, green, + inverse, + bgRed, red, + bgYellow, yellow, + black, white, + underline +} from 'kolorist' + +import readline from 'node:readline' + +/** + * Pills + */ + +const successPill = msg => bgGreen(black(` ${ msg } `)) +const infoPill = msg => inverse(` ${ msg } `) +const errorPill = msg => bgRed(white(` ${ msg } `)) +const warningPill = msg => bgYellow(black(` ${ msg } `)) + +/** + * Main approach + */ + +const dot = '•' +const banner = 'Quasar ' + dot +const greenBanner = green(banner) +const redBanner = red(banner) +const yellowBanner = yellow(banner) +const tipBanner = `${ green('App') } ${ dot } ${ successPill('TIP') } ${ dot } 🚀 ` + +const clearConsole = process.stdout.isTTY + ? () => { + // Fill screen with blank lines. Then move to 0 (beginning of visible part) and clear it + const blank = '\n'.repeat(process.stdout.rows) + console.log(blank) + readline.cursorTo(process.stdout, 0, 0) + readline.clearScreenDown(process.stdout) + } + : () => {} + +function tip (msg) { + console.log(msg ? ` ${ tipBanner } ${ msg }` : '') +} + +function log (msg) { + console.log(msg ? ` ${ greenBanner } ${ msg }` : '') +} + +function warn (msg, pill) { + if (msg !== void 0) { + const pillBanner = pill !== void 0 + ? bgYellow(black('', pill, '')) + ' ' + : '' + + console.warn(` ${ yellowBanner } ⚠️ ${ pillBanner }${ msg }`) + } + else { + console.warn() + } +} + +function fatal (msg, pill) { + if (msg !== void 0) { + const pillBanner = pill !== void 0 + ? errorPill(pill) + ' ' + : '' + + console.error(`\n ${ redBanner } ⚠️ ${ pillBanner }${ msg }\n`) + } + else { + console.error() + } + + process.exit(1) +} + +/** + * Extended approach - Status & pills + */ + +function success (msg, title = 'SUCCESS') { + console.log(` ${ greenBanner } ${ successPill(title) } ${ green(dot + ' ' + msg) }`) +} +function getSuccess (msg, title) { + return ` ${ greenBanner } ${ successPill(title) } ${ green(dot + ' ' + msg) }` +} + +function info (msg, title = 'INFO') { + console.log(` ${ greenBanner } ${ infoPill(title) } ${ green(dot) } ${ msg }`) +} +function getInfo (msg, title) { + return ` ${ greenBanner } ${ infoPill(title) } ${ green(dot) } ${ msg }` +} + +function error (msg, title = 'ERROR') { + console.log(` ${ redBanner } ${ errorPill(title) } ${ red(dot + ' ' + msg) }`) +} +function getError (msg, title = 'ERROR') { + return ` ${ redBanner } ${ errorPill(title) } ${ red(dot + ' ' + msg) }` +} + +function warning (msg, title = 'WARNING') { + console.log(` ${ yellowBanner } ${ warningPill(title) } ${ yellow(dot + ' ' + msg) }`) +} +function getWarning (msg, title = 'WARNING') { + return ` ${ yellowBanner } ${ warningPill(title) } ${ yellow(dot + ' ' + msg) }` +} + +/** + * Progress related + */ + +function progress (msg, token) { + const parseMsg = token !== void 0 + ? text => text.replace('___', underline(green(token))) + : text => text + + info(parseMsg(msg), 'WAIT') + + const startTime = Date.now() + + return msg => { + const diffTime = +new Date() - startTime + success(`${ parseMsg(msg) } ${ dot } ${ diffTime }ms`, 'DONE') + log() + } +} + +export default { + successPill, + infoPill, + errorPill, + warningPill, + + dot, + + clearConsole, + tip, + log, + warn, + fatal, + + success, + getSuccess, + + info, + getInfo, + + error, + getError, + + warning, + getWarning, + + progress +} diff --git a/docs/.eslintrc.js b/docs/.eslintrc.js index a612ee5298a..e97c3621c2a 100644 --- a/docs/.eslintrc.js +++ b/docs/.eslintrc.js @@ -2,11 +2,12 @@ module.exports = { root: true, parserOptions: { - parser: 'babel-eslint' + parser: '@babel/eslint-parser' }, env: { - browser: true + browser: true, + 'vue/setup-compiler-macros': true }, extends: [ @@ -53,6 +54,11 @@ module.exports = { 'import/no-extraneous-dependencies': 'off', 'quasar/check-valid-props': 'warn', + 'vue/multi-word-component-names': 'off', + 'multiline-ternary': 'off', + 'vue/no-mutating-props': 'off', + 'vue/no-v-text-v-html-on-component': 'off', + 'no-var': 'off', // allow console.log during development only 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', diff --git a/docs/package.json b/docs/package.json index 5d0caa42bfd..6dd4ed376cd 100644 --- a/docs/package.json +++ b/docs/package.json @@ -8,8 +8,7 @@ "scripts": { "dev": "quasar dev", "dev:ssr": "quasar dev -m ssr", - "build": "quasar build -m ssr", - "build:search": "node build/search.js", + "build": "NODE_OPTIONS=--openssl-legacy-provider quasar build -m pwa && node build/search.js", "lint": "eslint --ext .js,.vue src", "test": "echo \"No test specified\" && exit 0" }, @@ -20,17 +19,17 @@ "quasar": "^1.0.0" }, "devDependencies": { + "@babel/eslint-parser": "^7.13.14", "@quasar/app": "^2.0.0", - "babel-eslint": "^10.0.1", - "eslint": "^6.8.0", - "eslint-config-standard": "^14.1.0", - "eslint-loader": "^3.0.3", + "eslint": "^8.10.0", + "eslint-config-standard": "^17.0.0", "eslint-plugin-import": "^2.19.1", - "eslint-plugin-node": "^11.0.0", - "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", "eslint-plugin-quasar": "^1.0.0", "eslint-plugin-standard": "^4.0.0", - "eslint-plugin-vue": "^6.1.1", + "eslint-plugin-vue": "^9.0.0", + "eslint-webpack-plugin": "^2.0.0", "gray-matter": "^4.0.2", "markdown-ast": "^0.2.1", "markdown-it": "^10.0.0", diff --git a/docs/public/robots.txt b/docs/public/robots.txt new file mode 100644 index 00000000000..1f53798bb4f --- /dev/null +++ b/docs/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/docs/public/search_manifest.xml b/docs/public/search_manifest.xml index 935cdb84226..57e4ebc5266 100644 --- a/docs/public/search_manifest.xml +++ b/docs/public/search_manifest.xml @@ -2,5 +2,5 @@ Quasar docs Quasar documentation - + diff --git a/docs/quasar.conf.js b/docs/quasar.conf.js index 48140c5bfeb..79e641c7972 100644 --- a/docs/quasar.conf.js +++ b/docs/quasar.conf.js @@ -1,5 +1,6 @@ // Configuration for your app const path = require('path') +const ESLintPlugin = require('eslint-webpack-plugin') module.exports = function (ctx) { return { @@ -28,11 +29,14 @@ module.exports = function (ctx) { // analyze: true, chainWebpack (chain) { - chain.module.rule('eslint') - .pre() - .exclude.add(/node_modules|\.md\.js$/).end() - .test(/\.(js|vue)$/) - .use('eslint-loader').loader('eslint-loader') + // chain.module.rule('eslint') + // .pre() + // .exclude.add(/node_modules|\.md\.js$/).end() + // .test(/\.(js|vue)$/) + // .use('eslint-loader').loader('eslint-loader') + + chain.plugin('eslint-webpack-plugin') + .use(ESLintPlugin, [{ extensions: [ 'js', 'vue' ] }]) chain.resolve.alias .merge({ @@ -75,11 +79,11 @@ module.exports = function (ctx) { framework: { importStrategy: 'all', - iconSet: 'svg-mdi-v5', + iconSet: 'svg-mdi-v6', config: { loadingBar: { - color: 'amber' + color: 'brand-primary' } } }, @@ -87,7 +91,7 @@ module.exports = function (ctx) { animations: [ 'fadeIn', 'fadeOut' ], ssr: { - pwa: true + pwa: ctx.prod }, pwa: { diff --git a/docs/src-pwa/register-service-worker.js b/docs/src-pwa/register-service-worker.js index 85fc3d0d986..39019e56cf9 100644 --- a/docs/src-pwa/register-service-worker.js +++ b/docs/src-pwa/register-service-worker.js @@ -1,6 +1,6 @@ import { register } from 'register-service-worker' import { Notify } from 'quasar' -import { mdiCached } from '@quasar/extras/mdi-v5' +import { mdiCached } from '@quasar/extras/mdi-v6' // The ready(), registered(), cached(), updatefound() and updated() // events passes a ServiceWorkerRegistration instance in their arguments. diff --git a/docs/src-ssr/index.js b/docs/src-ssr/index.js index 9a17f728ee6..d27771a68ff 100644 --- a/docs/src-ssr/index.js +++ b/docs/src-ssr/index.js @@ -25,6 +25,10 @@ const serve = (path, cache) => express.static(ssr.resolveWWW(path), { maxAge: cache ? 1000 * 60 * 60 * 24 * 30 : 0 }) +// attackers can use this header to detect apps running Express +// and then launch specifically-targeted attacks +app.disable('x-powered-by') + // gzip app.use(compression({ threshold: 0 })) diff --git a/docs/src/assets/features.js b/docs/src/assets/features.js index 72ec6966cf9..c4dc9f83055 100644 --- a/docs/src/assets/features.js +++ b/docs/src/assets/features.js @@ -1,7 +1,7 @@ import menu from './menu.js' function normalizeComps (list) { - let acc = [ + const acc = [ { name: 'Layout', path: 'layout' @@ -30,14 +30,16 @@ function normalizeComps (list) { name: 'Page Scroller', path: 'page-scroller' } - ] + ].map(entry => ({ ...entry, path: `/layout/${entry.path}` })) list.forEach(entry => { if (entry.children) { - acc = acc.concat(entry.children) + acc.push( + ...(entry.children.map(entry => ({ ...entry, path: `/vue-components/${entry.path}` }))) + ) } else { - acc.push(entry) + acc.push({ ...entry, path: `/vue-components/${entry.path}` }) } }) @@ -46,6 +48,6 @@ function normalizeComps (list) { export default { comps: normalizeComps(menu.find(entry => entry.name === 'Vue Components').children), - dirs: menu.find(entry => entry.name === 'Vue Directives').children, - plugins: menu.find(entry => entry.name === 'Quasar Plugins').children + dirs: menu.find(entry => entry.name === 'Vue Directives').children.map(entry => ({ ...entry, path: `/vue-directives/${entry.path}` })), + plugins: menu.find(entry => entry.name === 'Quasar Plugins').children.map(entry => ({ ...entry, path: `/quasar-plugins/${entry.path}` })) } diff --git a/docs/src/assets/menu.js b/docs/src/assets/menu.js index 16d173de87a..dca80a535f9 100644 --- a/docs/src/assets/menu.js +++ b/docs/src/assets/menu.js @@ -349,7 +349,6 @@ const cli = [ }, { name: 'Capacitor versions', - badge: 'new', path: 'capacitor-version-support' }, { @@ -1193,8 +1192,14 @@ const utils = [ name: 'Scrolling Utils', path: 'scrolling-utils' }, + { + name: 'Type Checking Utils', + badge: 'new', + path: 'type-checking-utils' + }, { name: 'Other Utils', + badge: 'update', path: 'other-utils' } ] @@ -1218,7 +1223,6 @@ module.exports = [ { name: 'API Explorer', icon: 'travel_explore', - badge: 'new', path: 'api-explorer' }, { diff --git a/docs/src/components/AppMenu.js b/docs/src/components/AppMenu.js index 1e34bc925ba..f59b3e8ad91 100644 --- a/docs/src/components/AppMenu.js +++ b/docs/src/components/AppMenu.js @@ -7,7 +7,7 @@ import { QList } from 'quasar' -import { mdiArrowDownThinCircleOutline } from '@quasar/extras/mdi-v5' +import { mdiArrowDownThinCircleOutline } from '@quasar/extras/mdi-v6' import Menu from 'assets/menu.js' import './AppMenu.sass' diff --git a/docs/src/components/AppSearchResults.vue b/docs/src/components/AppSearchResults.vue index a14dd689e6a..c7d181bb153 100644 --- a/docs/src/components/AppSearchResults.vue +++ b/docs/src/components/AppSearchResults.vue @@ -36,7 +36,7 @@ + + diff --git a/docs/src/components/DocExample.vue b/docs/src/components/DocExample.vue index 6315a240001..9d7c8811974 100644 --- a/docs/src/components/DocExample.vue +++ b/docs/src/components/DocExample.vue @@ -18,11 +18,12 @@ q-card.doc-example.q-my-lg(:class="classes", flat, bordered) q-slide-transition div(v-show="expanded") q-tabs.doc-example__tabs( - v-model="currentTab", - align="left", - :active-color="dark ? 'amber' : void 0", - :indicator-color="dark ? 'amber' : 'brand-primary'", - dense, + v-model="currentTab" + align="left" + no-caps + :active-color="dark ? 'amber' : void 0" + :indicator-color="dark ? 'amber' : 'brand-primary'" + dense :breakpoint="0" ) q-tab( @@ -43,7 +44,7 @@ q-card.doc-example.q-my-lg(:class="classes", flat, bordered) :key="`pane-${tab}`" :name="tab" ) - doc-code(lang="markup") {{ parts[tab] }} + doc-code(lang="markup" :code="parts[tab]" max-height="70vh") q-separator.doc-example__separator @@ -59,7 +60,7 @@ import { openURL } from 'quasar' import { fabGithub, fabCodepen -} from '@quasar/extras/fontawesome-v5' +} from '@quasar/extras/fontawesome-v6' import { slugify } from 'assets/page-utils' @@ -90,7 +91,7 @@ export default { loading: true, component: null, tabs: [], - currentTab: 'template', + currentTab: 'Template', expanded: false, parts: {} } @@ -143,17 +144,21 @@ export default { methods: { parseComponent (comp) { - const - template = this.parseTemplate('template', comp), - script = this.parseTemplate('script', comp), - style = this.parseTemplate('style', comp) - this.parts = { - template, - script, - style + Template: this.parseTemplate('template', comp), + Script: this.parseTemplate('script', comp), + Style: this.parseTemplate('style', comp) } - this.tabs = [ 'template', 'script', 'style' ].filter(type => this.parts[type]) + + const tabs = [ 'Template', 'Script', 'Style' ] + .filter(type => this.parts[type]) + + if (tabs.length > 1) { + this.parts.All = comp + tabs.push('All') + } + + this.tabs = tabs }, parseTemplate (target, template) { @@ -166,7 +171,7 @@ export default { }, openGitHub () { - openURL(`https://github.com/quasarframework/quasar/tree/dev/docs/src/examples/${this.file}.vue`) + openURL(`https://github.com/quasarframework/quasar/tree/v1/docs/src/examples/${this.file}.vue`) }, openCodepen () { diff --git a/docs/src/components/DocInstallation.vue b/docs/src/components/DocInstallation.vue index ff7d9fcea53..2869ee61923 100644 --- a/docs/src/components/DocInstallation.vue +++ b/docs/src/components/DocInstallation.vue @@ -1,6 +1,6 @@ + + diff --git a/docs/src/components/page-parts/tooltip/TooltipPositioning.vue b/docs/src/components/page-parts/tooltip/TooltipPositioning.vue index 8d8463fd431..88841c9d815 100644 --- a/docs/src/components/page-parts/tooltip/TooltipPositioning.vue +++ b/docs/src/components/page-parts/tooltip/TooltipPositioning.vue @@ -1,51 +1,51 @@ diff --git a/docs/src/components/page-parts/transitions/TransitionList.vue b/docs/src/components/page-parts/transitions/TransitionList.vue index 573503c033e..2ce30c3fe25 100644 --- a/docs/src/components/page-parts/transitions/TransitionList.vue +++ b/docs/src/components/page-parts/transitions/TransitionList.vue @@ -1,21 +1,21 @@ @@ -23,7 +23,7 @@ export default { data () { return { - url: 'https://placeimg.com/500/300/nature', + url: 'https://picsum.photos/500/300', transitions: [ 'slide-right', 'slide-left', @@ -46,7 +46,7 @@ export default { methods: { trigger () { - this.url = 'https://placeimg.com/500/300/nature?t=' + Math.random() + this.url = 'https://picsum.photos/500/300?t=' + Math.random() } } } diff --git a/docs/src/components/page-parts/typography/TypographyHeadings.vue b/docs/src/components/page-parts/typography/TypographyHeadings.vue index dd46a01bd0d..4727b353303 100644 --- a/docs/src/components/page-parts/typography/TypographyHeadings.vue +++ b/docs/src/components/page-parts/typography/TypographyHeadings.vue @@ -1,11 +1,11 @@ diff --git a/docs/src/examples/MorphUtils/SameElement.vue b/docs/src/examples/MorphUtils/SameElement.vue index 10debcb362f..75548af7312 100644 --- a/docs/src/examples/MorphUtils/SameElement.vue +++ b/docs/src/examples/MorphUtils/SameElement.vue @@ -34,28 +34,28 @@ export default { props1 () { return this.toggle1 === true ? { - class: 'q-ml-sm q-pa-md bg-orange text-white rounded-borders', - style: 'font-size: 24px' - } + class: 'q-ml-sm q-pa-md bg-orange text-white rounded-borders', + style: 'font-size: 24px' + } : { - class: 'q-ml-xl q-px-xl q-py-lg bg-blue text-white', - style: 'border-radius: 25% 0/50% 0; font-size: 36px' - } + class: 'q-ml-xl q-px-xl q-py-lg bg-blue text-white', + style: 'border-radius: 25% 0/50% 0; font-size: 36px' + } }, props2 () { return this.toggle2 === true ? { - fontSize: '52px', - color: 'positive', - icon: 'check', - rounded: true - } + fontSize: '52px', + color: 'positive', + icon: 'check', + rounded: true + } : { - fontSize: '32px', - color: 'negative', - icon: 'close' - } + fontSize: '32px', + color: 'negative', + icon: 'close' + } } }, diff --git a/docs/src/examples/Notify/Positioning.vue b/docs/src/examples/Notify/Positioning.vue index 698cbed822c..a3cdfa0721d 100644 --- a/docs/src/examples/Notify/Positioning.vue +++ b/docs/src/examples/Notify/Positioning.vue @@ -87,13 +87,13 @@ export default { multiLine, actions: twoActions ? [ - { label: 'Reply', color: buttonColor, handler: () => { /* console.log('wooow') */ } }, - { label: 'Dismiss', color: 'yellow', handler: () => { /* console.log('wooow') */ } } - ] + { label: 'Reply', color: buttonColor, handler: () => { /* console.log('wooow') */ } }, + { label: 'Dismiss', color: 'yellow', handler: () => { /* console.log('wooow') */ } } + ] : (random > 40 - ? [ { label: 'Reply', color: buttonColor, handler: () => { /* console.log('wooow') */ } } ] - : null - ), + ? [ { label: 'Reply', color: buttonColor, handler: () => { /* console.log('wooow') */ } } ] + : null + ), timeout: Math.random() * 5000 + 3000 }) } diff --git a/docs/src/examples/QAvatar/Overlapping.vue b/docs/src/examples/QAvatar/Overlapping.vue new file mode 100644 index 00000000000..a8289d1dbb6 --- /dev/null +++ b/docs/src/examples/QAvatar/Overlapping.vue @@ -0,0 +1,19 @@ + + + diff --git a/docs/src/examples/QBar/MacOS.vue b/docs/src/examples/QBar/MacOS.vue index dd6edcda9e4..467682b6d4a 100644 --- a/docs/src/examples/QBar/MacOS.vue +++ b/docs/src/examples/QBar/MacOS.vue @@ -59,7 +59,7 @@ diff --git a/docs/src/examples/QBtn/Links.vue b/docs/src/examples/QBtn/Links.vue index 5dc22da4585..f2265b5b709 100644 --- a/docs/src/examples/QBtn/Links.vue +++ b/docs/src/examples/QBtn/Links.vue @@ -3,8 +3,8 @@ - - + + diff --git a/docs/src/examples/QBtn/LinksWithGo.vue b/docs/src/examples/QBtn/LinksWithGo.vue new file mode 100644 index 00000000000..5c80bea789b --- /dev/null +++ b/docs/src/examples/QBtn/LinksWithGo.vue @@ -0,0 +1,42 @@ + + + diff --git a/docs/src/examples/QCarousel/MultiImageSlides.vue b/docs/src/examples/QCarousel/MultiImageSlides.vue index 03e885170f4..36f928e9715 100644 --- a/docs/src/examples/QCarousel/MultiImageSlides.vue +++ b/docs/src/examples/QCarousel/MultiImageSlides.vue @@ -16,13 +16,17 @@
- + +
With native context menu and not draggable - can be swiped
+
- + +
With native context menu and draggable - cannot be swiped
+
diff --git a/docs/src/examples/QCheckbox/Label.vue b/docs/src/examples/QCheckbox/Label.vue index 0b0b7a234bd..c5a5e43e657 100644 --- a/docs/src/examples/QCheckbox/Label.vue +++ b/docs/src/examples/QCheckbox/Label.vue @@ -1,13 +1,34 @@ @@ -17,7 +38,9 @@ export default { data () { return { left: true, - right: false + right: false, + left2: true, + right2: false } } } diff --git a/docs/src/examples/QCheckbox/Standard.vue b/docs/src/examples/QCheckbox/Standard.vue index fd4804675a7..d351c0b7f06 100644 --- a/docs/src/examples/QCheckbox/Standard.vue +++ b/docs/src/examples/QCheckbox/Standard.vue @@ -1,11 +1,6 @@ diff --git a/docs/src/examples/QCheckbox/WithIcons.vue b/docs/src/examples/QCheckbox/WithIcons.vue new file mode 100644 index 00000000000..f4b53656743 --- /dev/null +++ b/docs/src/examples/QCheckbox/WithIcons.vue @@ -0,0 +1,20 @@ + + + diff --git a/docs/src/examples/QCircularProgress/Indeterminate.vue b/docs/src/examples/QCircularProgress/Indeterminate.vue index d7d0121d4fe..14613686ace 100644 --- a/docs/src/examples/QCircularProgress/Indeterminate.vue +++ b/docs/src/examples/QCircularProgress/Indeterminate.vue @@ -2,6 +2,7 @@
+
+ + + + + + + +
+ + + diff --git a/docs/src/examples/QColor/NoHeaderFooter.vue b/docs/src/examples/QColor/NoHeaderFooter.vue index f6c6d479e17..7932362301f 100644 --- a/docs/src/examples/QColor/NoHeaderFooter.vue +++ b/docs/src/examples/QColor/NoHeaderFooter.vue @@ -1,6 +1,7 @@