diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 117863c..bb42a38 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -12,9 +12,9 @@ Hi! I’m really excited that you are interested in contributing to Quasar. Befo - The issue list of this repo is **exclusively** for bug reports and feature requests. Non-conforming issues will be closed immediately. - - For simple beginner questions, you can get quick answers from the [Quasar Discord chat](http://chat.quasar-framework.org). + - For simple beginner questions, you can get quick answers from the [Quasar Discord chat](https://chat.quasar.dev). - - For more complicated questions, you can use [the official forum](https://forum.quasar-framework.org/). Make sure to provide enough information when asking your questions - this makes it easier for others to help you! + - 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`). diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b9cd9bb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,8 @@ +# These are supported funding model platforms + +github: rstoenescu +patreon: # quasarframework +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +custom: # Replace with a single custom sponsorship URL diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index ff26388..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,42 +0,0 @@ - - - -### Software version - -Quasar: -vue-cli-plugin-quasar: -OS: -Node: -NPM: -Any other software related to your bug: - -### What did you get as the error? - -### What were you expecting? - -### What steps did you take, to get the error? diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..b56aec5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,37 @@ +--- +name: Bug report +about: Create a report to help us improve Quasar +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Platform (please complete the following information):** +OS: +Node: +NPM: +Yarn: +Browsers: +iOS: +Android: +Electron: + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..fab5ddc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for Quasar +title: '' +labels: feature request +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9200b92..4d5a5db 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -27,7 +27,7 @@ If yes, please describe the impact and migration path for existing applications: - [ ] It's submitted to the `dev` branch and _not_ the `master` 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 with all Quasar themes -- [ ] Any necessary documentation has been added or updated [in the docs](https://github.com/quasarframework/quasar-framework.org/tree/dev/source) (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/dev/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/README.md b/README.md index 8a83fcf..09dc76e 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ -![Quasar Framework logo](https://cdn.rawgit.com/quasarframework/quasar-art/863c14bd/dist/svg/quasar-logo-full-inline.svg) +![Quasar Framework logo](https://cdn.quasar.dev/logo-v2/header.png) # Quasar Framework: vue-cli-plugin-quasar -> :rocket: Start building a Vue app with Quasar Framework in 2 minutes! +> :rocket: Start building a Vue app with Quasar Framework v2 in 2 minutes! > :warning: **For the premium (and recommended) experience with Quasar, including the ability to build Mobile & Electron apps and efortless upgrades to new Quasar versions, you should instead use Quasar CLI** [![Join the chat at https://discord.gg/5TDhbDg](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/5TDhbDg) - + Please submit a PR to https://github.com/quasarframework/quasar-awesome with your website/app/Quasar tutorial/video etc. Thank you! ## Getting started -:warning: Make sure you have vue-cli 3.x.x: +:warning: Make sure you have vue-cli v5: ``` vue --version ``` -If you don't have a project created with vue-cli 3.x yet: +If you don't have a project created with vue-cli v5 yet: ``` vue create my-app @@ -29,24 +29,23 @@ vue create my-app Navigate to the newly created project folder and add the cli plugin. Before installing it, make sure to commit your current changes should you wish to revert them later. ``` -cd my-app -vue add quasar +$ cd my-app +$ yarn add --dev vue-cli-plugin-quasar +$ vue invoke quasar ``` It will ask you if you want the plugin to replace some existing files. It is recommended that you do it if you wish to have an example so you can quickly develop your app. -Your Vue config (in package.json or vue.config.js file, depending on what you chose when you created your vue app) will also contain a `quasar` Object. Most important property is `theme` (with possible values "mat" or "ios"), which you can later change should you want. +Your Vue config (in package.json or vue.config.js file, depending on what you chose when you created your vue app) will also contain a `quasar` Object. ## Supporting Quasar -Quasar Framework is an MIT-licensed open source project. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rstoenescu/quasar-framework/blob/dev/backers.md). If you'd like to join them, check out [Quasar Framework's Patreon campaign](https://www.patreon.com/quasarframework). +Quasar Framework is an MIT-licensed open source project. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rstoenescu/quasar-framework/blob/dev/backers.md). -### Proudly sponsored by: - -[![Truelogic](http://quasar-framework.org/images/logo_truelogic.png)](http://truelogic.com) +**Please read our manifest on [Why donations are important](https://quasar.dev/why-donate)**. If you'd like to become a donator, check out [Quasar Framework's Donator campaign](https://donate.quasar.dev). ## Documentation -Head on to the Quasar Framework official website: [http://quasar-framework.org](http://quasar-framework.org) +Head on to the Quasar Framework official website: [https://quasar.dev](https://quasar.dev) ## Stay in Touch @@ -54,13 +53,11 @@ For latest releases and announcements, follow on Twitter: [@quasarframework](htt ## Community Forum -Head on to the official community forum: [http://forum.quasar-framework.org](http://forum.quasar-framework.org) +Head on to the official community forum: [https://forum.quasar.dev](https://forum.quasar.dev) ## Quasar Repositories -* [Quasar Framework](https://github.com/quasarframework/quasar) -* [Quasar CLI](https://github.com/quasarframework/quasar-cli) -* [Quasar Play App](https://github.com/quasarframework/quasar-play) +* [Quasar Framework](https://github.com/quasarframework) ## Contributing @@ -70,7 +67,7 @@ I'm excited if you want to contribute to Quasar under any form (report bugs, wri **Please use the appropriate Github repo to report issues. See "Related Components" above.** For example, a bug related to CLI should be reported to the CLI repo, one related to build issues to Quasar Framework Templates repo and so on. -- The issue list of the repository is **exclusively** for bug reports and feature requests. For anything else please use the [Community Forum](http://forum.quasar-framework.org). +- The issue list of the repository is **exclusively** for bug reports and feature requests. For anything else please use the [Community Forum](https://forum.quasar.dev). - Try to search for your issue, it may have already been fixed in the development branch or it may have a resolution. @@ -80,8 +77,6 @@ I'm excited if you want to contribute to Quasar under any form (report bugs, wri - If your issue is resolved but still open, don’t hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it. -Read more [here](http://quasar-framework.org/guide/quasar-contribution-guide.html). - ## License Copyright (c) 2016-present Razvan Stoenescu diff --git a/generator/index.js b/generator/index.js index ce865ef..f97b1d3 100644 --- a/generator/index.js +++ b/generator/index.js @@ -1,15 +1,15 @@ -const - fs = require('fs'), - extendPluginOptions = require('../lib/extendPluginOptions'), - extendBabel = require('../lib/extendBabel') +const fs = require('fs') +const extendPluginOptions = require('../lib/extendPluginOptions') const message = ` -Documentation can be found at: https://quasar-framework.org +Documentation can be found at: https://quasar.dev -Quasar is relying on donations to evolve. We'd be very grateful if you can -take a look at: https://www.patreon.com/quasarframework -Any amount is very welcomed. -If invoices are required, please first contact razvan.stoenescu@gmail.com +Quasar is relying on donations to evolve. We'd be very grateful +if you can read our manifest on "Why donations are important": +https://quasar.dev/why-donate +Donation campaign: https://donate.quasar.dev Any amount is very +welcomed. If invoices are required, please first contact +razvan@quasar.dev Please give us a star on Github if you appreciate our work: https://github.com/quasarframework/quasar @@ -17,158 +17,134 @@ https://github.com/quasarframework/quasar Enjoy! - Quasar Team ` -module.exports = (api, opts, rootOpts) => { - const - components = [ - 'QBtn', - 'QLayout', - 'QLayoutHeader', - 'QLayoutDrawer', - 'QPage', - 'QPageContainer', - 'QToolbar', - 'QToolbarTitle', - 'QList', - 'QListHeader', - 'QItemSeparator', - 'QItem', - 'QItemSide', - 'QItemMain', - ], - directives = [], - plugins = [] +const iconMap = { + ionicons: 'ionicons-v4', + fontawesome: 'fontawesome-v5', + mdi: 'mdi-v4' +} +const plugins = [] + +module.exports = (api, opts) => { const + quasarPath = api.resolve('./src/quasar-user-options.js'), tsPath = api.resolve('./src/main.ts'), jsPath = api.resolve('./src/main.js'), hasTS = fs.existsSync(tsPath) + const dependencies = { + quasar: '^2.16.0', + '@quasar/extras': '^1.0.0' + } + const deps = { - dependencies: { - 'quasar-framework': '^0.17.0', - 'quasar-extras': '^2.0.4' - }, - devDependencies: { - 'babel-plugin-transform-imports': '1.5.0', - 'stylus': '^0.54.5', - 'stylus-loader': '^3.0.2' - } + dependencies, + devDependencies: {} + } + + if (['sass', 'scss'].includes(opts.quasar.cssPreprocessor)) { + Object.assign(deps.devDependencies, { + 'sass': '^1.78.0', + 'sass-loader': '^14.2.1' + }) } if (opts.quasar.rtlSupport) { - deps.devDependencies['postcss-rtl'] = '^1.2.3' + deps.devDependencies['postcss-rtlcss'] = '^5.4.0' } api.extendPackage(deps) // modify plugin options extendPluginOptions(api, (pluginOptions, transpileDependencies) => { - pluginOptions.quasar = { - theme: opts.quasar.theme - } - if (opts.quasar.rtlSupport) { - pluginOptions.quasar.rtlSupport = true - } - if (opts.quasar.all) { - pluginOptions.quasar.importAll = true - } + pluginOptions.quasar = Object.assign( + pluginOptions.quasar || {}, + { + importStrategy: 'kebab', + rtlSupport: opts.quasar.rtlSupport + } + ) - transpileDependencies.push(/[\\/]node_modules[\\/]quasar-framework[\\/]/) + transpileDependencies.push('quasar') return { pluginOptions, transpileDependencies } }) api.render('./templates/common') + if (opts.quasar.rtlSupport) { api.render('./templates/rtl') } + + if (opts.quasar.cssPreprocessor !== 'none') { + api.render(`./templates/${opts.quasar.cssPreprocessor}`) + } + if (opts.quasar.replaceComponents) { - const - extension = hasTS ? 'ts' : 'js', + const extension = hasTS ? 'ts' : 'js', routerFile = api.resolve(`src/router.${extension}`), hasRouter = fs.existsSync(routerFile) - api.render(`./templates/with${hasRouter ? '' : 'out'}-router`, opts) + api.render(`./templates/with${hasRouter ? '' : 'out'}-router-base`, opts) + api.render( + `./templates/with${hasRouter ? '' : 'out'}-router`, + opts + ) if (hasRouter) { api.render(`./templates/with-router-${extension}`) } } api.onCreateComplete(() => { - if (!opts.quasar.all) { - extendBabel(api, opts.quasar.theme) - } - - let lines = '\n' + let qFileLines = '' const - hasLang = opts.quasar.i18n !== 'en-us', - hasIconSet = opts.quasar.iconSet !== 'material-icons' + hasIconSet = opts.quasar.iconSet !== 'material-icons', + hasLang = opts.quasar.lang !== 'en-US' if (!opts.quasar.features.includes(opts.quasar.iconSet)) { opts.quasar.features.push(opts.quasar.iconSet) } - lines += `\nimport './styles/quasar.styl'` - - if (opts.quasar.features.includes('ie')) { - lines += `\nimport 'quasar-framework/dist/quasar.ie.polyfills'` + if (opts.quasar.cssPreprocessor !== 'none') { + qFileLines += `\nimport './styles/quasar.${opts.quasar.cssPreprocessor}'` } + else { + qFileLines += `\nimport 'quasar/dist/quasar.css'` + } + if (hasIconSet) { - lines += `\nimport iconSet from 'quasar-framework/icons/${opts.quasar.iconSet}'` + const set = iconMap[opts.quasar.iconSet] || opts.quasar.iconSet + qFileLines += `\nimport iconSet from 'quasar/icon-set/${set}.js'` } + if (hasLang) { - lines += `\nimport lang from 'quasar-framework/i18n/${opts.quasar.i18n}'` + qFileLines += `\nimport lang from 'quasar/lang/${opts.quasar.lang}.js'` } + opts.quasar.features - .filter(feat => feat !== 'ie') .forEach(feat => { - lines += `\nimport 'quasar-extras/${feat}'` + feat = iconMap[feat] || feat + qFileLines += `\nimport '@quasar/extras/${feat}/${feat}.css'` }) - // build import - lines += `\nimport ` - if (opts.quasar.all) { - lines += `Quasar` - } - else { - lines += `{\n Quasar, ` - components.concat(directives).concat(plugins) - .forEach(part => { lines += `\n ${part},` }) - lines += `\n}` - } - lines += ` from 'quasar'` - - // build Vue.use() - lines += `\n\nVue.use(Quasar, {` - lines += `\n config: {}` - - // if not 'all' we want to include specific defaults - if (!opts.quasar.all) { - lines+= ',\n components: {' - components - .forEach(part => { lines += `\n ${part},` }) - lines += `\n }` - - lines+= ',\n directives: {' - directives - .forEach(part => { lines += `\n ${part},` }) - lines += `\n }` - - lines+= ',\n plugins: {' - plugins - .forEach(part => { lines += `\n ${part},` }) - lines += `\n }` - } + qFileLines += `\n\n// To be used on app.use(Quasar, { ... })\nexport default {` + qFileLines += `\n config: {}` + + qFileLines += ',\n plugins: {' + plugins.forEach(part => { + qFileLines += `\n ${part},` + }) + qFileLines += `\n }` if (hasLang) { - lines += `, i18n: lang` + qFileLines += `,\n lang: lang` } if (hasIconSet) { - lines += `, iconSet: iconSet` + qFileLines += `,\n iconSet: iconSet` } - lines += `\n })` + qFileLines += `\n}` // Now inject additions to main.[js|ts] { @@ -178,10 +154,13 @@ module.exports = (api, opts, rootOpts) => { const mainLines = content.split(/\r?\n/g).reverse() const index = mainLines.findIndex(line => line.match(/^import/)) - mainLines[index] += lines + mainLines[index] += `\nimport { Quasar } from 'quasar'\nimport quasarUserOptions from './quasar-user-options'` content = mainLines.reverse().join('\n') + content = content.replace('createApp(App)', `createApp(App).use(Quasar, quasarUserOptions)`) fs.writeFileSync(mainPath, content, { encoding: 'utf8' }) + + fs.writeFileSync(quasarPath, qFileLines, { encoding: 'utf8' }) } if (api.generator.hasPlugin('@vue/cli-plugin-eslint')) { diff --git a/generator/templates/common/public/favicon.ico b/generator/templates/common/public/favicon.ico index b3446a1..ae7bbdb 100644 Binary files a/generator/templates/common/public/favicon.ico and b/generator/templates/common/public/favicon.ico differ diff --git a/generator/templates/common/src/assets/logo.png b/generator/templates/common/src/assets/logo.png deleted file mode 100644 index 2a9b50b..0000000 Binary files a/generator/templates/common/src/assets/logo.png and /dev/null differ diff --git a/generator/templates/common/src/assets/logo.svg b/generator/templates/common/src/assets/logo.svg new file mode 100644 index 0000000..8210831 --- /dev/null +++ b/generator/templates/common/src/assets/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/generator/templates/common/src/styles/quasar.variables.styl b/generator/templates/common/src/styles/quasar.variables.styl deleted file mode 100644 index b1545ef..0000000 --- a/generator/templates/common/src/styles/quasar.variables.styl +++ /dev/null @@ -1,14 +0,0 @@ -// It's highly recommended to change the default colors -// to match your app's branding. - -$primary = #027be3 -$secondary = #26A69A -$tertiary = #555 - -$neutral = #E0E1E2 -$positive = #21BA45 -$negative = #DB2828 -$info = #31CCEC -$warning = #F2C037 - -@import '~quasar-variables' diff --git a/generator/templates/rtl/_postcssrc.js b/generator/templates/rtl/_postcssrc.js index 4759ab9..818be30 100644 --- a/generator/templates/rtl/_postcssrc.js +++ b/generator/templates/rtl/_postcssrc.js @@ -4,7 +4,7 @@ const plugins = [ if (process.env.QUASAR_RTL) { plugins.push( - require('postcss-rtl')({}) + require('postcss-rtlcss')({}) ) } diff --git a/generator/templates/common/src/styles/quasar.styl b/generator/templates/sass/src/styles/quasar.sass similarity index 61% rename from generator/templates/common/src/styles/quasar.styl rename to generator/templates/sass/src/styles/quasar.sass index 8b325c0..1ba19fe 100644 --- a/generator/templates/common/src/styles/quasar.styl +++ b/generator/templates/sass/src/styles/quasar.sass @@ -1,3 +1,3 @@ -@import './quasar.variables' +@import './quasar.variables.sass' @import '~quasar-styl' // @import '~quasar-addon-styl' diff --git a/generator/templates/sass/src/styles/quasar.variables.sass b/generator/templates/sass/src/styles/quasar.variables.sass new file mode 100644 index 0000000..b07df8a --- /dev/null +++ b/generator/templates/sass/src/styles/quasar.variables.sass @@ -0,0 +1,15 @@ +// It's highly recommended to change the default colors +// to match your app's branding. + +$primary : #027BE3 +$secondary : #26A69A +$accent : #9C27B0 + +$dark : #1D1D1D + +$positive : #21BA45 +$negative : #C10015 +$info : #31CCEC +$warning : #F2C037 + +@import '~quasar-variables-styl' diff --git a/generator/templates/scss/src/styles/quasar.scss b/generator/templates/scss/src/styles/quasar.scss new file mode 100644 index 0000000..7991e66 --- /dev/null +++ b/generator/templates/scss/src/styles/quasar.scss @@ -0,0 +1,3 @@ +@import './quasar.variables.scss'; +@import '~quasar-styl'; +// @import '~quasar-addon-styl'; diff --git a/generator/templates/scss/src/styles/quasar.variables.scss b/generator/templates/scss/src/styles/quasar.variables.scss new file mode 100644 index 0000000..115c79a --- /dev/null +++ b/generator/templates/scss/src/styles/quasar.variables.scss @@ -0,0 +1,15 @@ +// It's highly recommended to change the default colors +// to match your app's branding. + +$primary : #027BE3; +$secondary : #26A69A; +$accent : #9C27B0; + +$dark : #1D1D1D; + +$positive : #21BA45; +$negative : #C10015; +$info : #31CCEC; +$warning : #F2C037; + +@import '~quasar-variables-styl' diff --git a/generator/templates/with-router-base/src/App.vue b/generator/templates/with-router-base/src/App.vue new file mode 100644 index 0000000..083049b --- /dev/null +++ b/generator/templates/with-router-base/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/generator/templates/with-router/src/views/About.vue b/generator/templates/with-router-base/src/views/About.vue similarity index 100% rename from generator/templates/with-router/src/views/About.vue rename to generator/templates/with-router-base/src/views/About.vue diff --git a/generator/templates/with-router/src/views/Home.vue b/generator/templates/with-router-base/src/views/Home.vue similarity index 62% rename from generator/templates/with-router/src/views/Home.vue rename to generator/templates/with-router-base/src/views/Home.vue index f5ccfd1..95a2ece 100644 --- a/generator/templates/with-router/src/views/Home.vue +++ b/generator/templates/with-router-base/src/views/Home.vue @@ -1,6 +1,6 @@ diff --git a/generator/templates/with-router-js/src/router.js b/generator/templates/with-router-js/src/router.js index 9826928..bd939f2 100644 --- a/generator/templates/with-router-js/src/router.js +++ b/generator/templates/with-router-js/src/router.js @@ -1,28 +1,30 @@ -import Vue from 'vue' -import Router from 'vue-router' +import { createRouter, createWebHistory } from 'vue-router' import DefaultLayout from './layouts/Default.vue' import Home from './views/Home.vue' import About from './views/About.vue' -Vue.use(Router) +const routes = [ + { + path: '/', + component: DefaultLayout, + children: [ + { + path: '', + name: 'home', + component: Home + }, + { + path: '/about', + name: 'about', + component: About + } + ] + } +] -export default new Router({ - routes: [ - { - path: '/', - component: DefaultLayout, - children: [ - { - path: '', - name: 'home', - component: Home - }, - { - path: '/about', - name: 'about', - component: About - } - ] - } - ] +const router = createRouter({ + history: createWebHistory(process.env.BASE_URL), + routes }) + +export default router diff --git a/generator/templates/with-router-ts/src/router.ts b/generator/templates/with-router-ts/src/router.ts index 9826928..928341c 100644 --- a/generator/templates/with-router-ts/src/router.ts +++ b/generator/templates/with-router-ts/src/router.ts @@ -1,28 +1,30 @@ -import Vue from 'vue' -import Router from 'vue-router' +import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import DefaultLayout from './layouts/Default.vue' import Home from './views/Home.vue' import About from './views/About.vue' -Vue.use(Router) +const routes: Array = [ + { + path: '/', + component: DefaultLayout, + children: [ + { + path: '', + name: 'home', + component: Home + }, + { + path: '/about', + name: 'about', + component: About + } + ] + } +] -export default new Router({ - routes: [ - { - path: '/', - component: DefaultLayout, - children: [ - { - path: '', - name: 'home', - component: Home - }, - { - path: '/about', - name: 'about', - component: About - } - ] - } - ] +const router = createRouter({ + history: createWebHistory(process.env.BASE_URL), + routes }) + +export default router diff --git a/generator/templates/with-router/src/App.vue b/generator/templates/with-router/src/App.vue deleted file mode 100644 index 96ba949..0000000 --- a/generator/templates/with-router/src/App.vue +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/generator/templates/with-router/src/layouts/Default.vue b/generator/templates/with-router/src/layouts/Default.vue index b4d32eb..115d545 100644 --- a/generator/templates/with-router/src/layouts/Default.vue +++ b/generator/templates/with-router/src/layouts/Default.vue @@ -1,68 +1,97 @@ - - diff --git a/generator/templates/without-router/src/components/HelloWorld.vue b/generator/templates/without-router-base/src/components/HelloWorld.vue similarity index 62% rename from generator/templates/without-router/src/components/HelloWorld.vue rename to generator/templates/without-router-base/src/components/HelloWorld.vue index d94a956..a9f95db 100644 --- a/generator/templates/without-router/src/components/HelloWorld.vue +++ b/generator/templates/without-router-base/src/components/HelloWorld.vue @@ -1,6 +1,6 @@ diff --git a/generator/templates/without-router/src/App.vue b/generator/templates/without-router/src/App.vue index 78a6810..a3187da 100644 --- a/generator/templates/without-router/src/App.vue +++ b/generator/templates/without-router/src/App.vue @@ -1,57 +1,79 @@ - - diff --git a/index.js b/index.js index 46f8c2b..cec31bf 100644 --- a/index.js +++ b/index.js @@ -1,44 +1,106 @@ +const fs = require('fs') +const path = require('path') +const webpack = require('webpack') +const { merge } = require('webpack-merge') + +const getDevlandFile = require('./lib/get-devland-file') +const { version } = getDevlandFile('quasar/package.json') + +const transformAssetUrls = getDevlandFile('quasar/dist/transforms/loader-asset-urls.json') + +function getCssPreprocessor (api) { + return ['sass', 'scss'].find(ext => { + return fs.existsSync( + api.resolve('src/styles/quasar.variables.' + ext) + ) + }) +} + +function applyCssRule (rule, cssPreprocessor) { + rule + .use('quasar-sass-variables-loader') + .loader(path.join(__dirname, `lib/loader.${cssPreprocessor}.js`)) +} + +function applyCssLoaders (chain, cssPreprocessor) { + const rule = chain.module.rule(cssPreprocessor) + + applyCssRule(rule.oneOf('vue-modules'), cssPreprocessor) + applyCssRule(rule.oneOf('vue'), cssPreprocessor) + applyCssRule(rule.oneOf('normal-modules'), cssPreprocessor) + applyCssRule(rule.oneOf('normal'), cssPreprocessor) +} + module.exports = (api, options) => { if (options.pluginOptions.quasar.rtlSupport) { process.env.QUASAR_RTL = true } + const cssPreprocessor = getCssPreprocessor(api) + api.chainWebpack(chain => { - const - theme = options.pluginOptions.quasar.theme, - importAll = options.pluginOptions.quasar.importAll - - if (!importAll) { - chain.resolve.extensions - .prepend(`.${theme}.js`) - - chain.plugin('define') - .tap(args => { - const { 'process.env': env, ...rest } = args[0] - return [{ - 'process.env': Object.assign( - {}, - env, - { THEME: JSON.stringify(theme) } - ), - ...rest - }] - }) + if (cssPreprocessor) { + chain.resolve.alias + .set( + 'quasar-variables', + api.resolve(`src/styles/quasar.variables.${cssPreprocessor}`) + ) + .set( + 'quasar-variables-styl', + `quasar/src/css/variables.sass` + ) + .set( + 'quasar-styl', + `quasar/dist/quasar.sass` + ) + .set( + 'quasar-addon-styl', + `quasar/src/css/flex-addon.sass` + ) + + applyCssLoaders(chain, 'sass') + applyCssLoaders(chain, 'scss') } - chain.resolve.alias - .set( - 'quasar', - importAll - ? api.resolve(`node_modules/quasar-framework/dist/quasar.${theme}.esm.js`) - : 'quasar-framework' + chain.plugin('define-quasar') + .use(webpack.DefinePlugin, [{ + __QUASAR_VERSION__: `'${version}'`, + __QUASAR_SSR__: false, + __QUASAR_SSR_SERVER__: false, + __QUASAR_SSR_CLIENT__: false, + __QUASAR_SSR_PWA__: false, + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false + }]) + + chain.performance.maxEntrypointSize(512000) + + const strategy = options.pluginOptions.quasar.importStrategy || 'kebab' + + chain.module.rule('vue').use('vue-loader').tap(options => ({ + ...options, + transformAssetUrls: merge( + options.transformAssetUrls || {}, + transformAssetUrls ) - .set('variables', api.resolve(`src/styles/quasar.variables.styl`)) - .set('quasar-variables', api.resolve(`node_modules/quasar-framework/src/css/core.variables.styl`)) - .set('quasar-styl', api.resolve(`node_modules/quasar-framework/dist/quasar.${theme}.styl`)) - .set('quasar-addon-styl', api.resolve(`node_modules/quasar-framework/src/css/flex-addon.styl`)) + })) + + if (['kebab', 'pascal', 'combined'].includes(strategy)) { + chain.module.rule('vue') + .use('vue-auto-import-quasar') + .loader(path.join(__dirname, 'lib/loader.vue.auto-import-quasar.js')) + .options({ strategy }) + .before('vue-loader') - chain.performance - .maxEntrypointSize(512000) + chain.module.rule('js-transform-quasar-imports') + .test(/\.(t|j)sx?$/) + .use('js-transform-quasar-imports') + .loader(path.join(__dirname, 'lib/loader.js.transform-quasar-imports.js')) + } + else { + console.error(`Incorrect setting for quasar > importStrategy (${strategy})`) + console.error(`Use one of: 'kebab', 'pascal', 'combined'.`) + console.log() + process.exit(1) + } }) } diff --git a/lib/extendBabel.js b/lib/extendBabel.js deleted file mode 100644 index f64db90..0000000 --- a/lib/extendBabel.js +++ /dev/null @@ -1,36 +0,0 @@ -const fs = require('fs') - -function updateBabel (babel, theme) { - babel.plugins = babel.plugins || [] - babel.plugins.push([ - 'transform-imports', { - quasar: { - transform: `quasar-framework/dist/babel-transforms/imports.${theme}.js`, - preventFullImport: true - } - } - ]) - return babel -} - -module.exports = function (api, theme) { - const - babelPath = api.resolve('babel.config.js'), - packageJsonPath = api.resolve('package.json') - - let content, filePath - - if (fs.existsSync(babelPath)) { - filePath = babelPath - const config = updateBabel(require(filePath), theme) - content = `module.exports = ${JSON.stringify(config, null, 2)}` - } - else if (fs.existsSync(packageJsonPath)) { - filePath = packageJsonPath - const config = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) - config.babel = updateBabel(config.babel || {}, theme) - content = JSON.stringify(config, null, 2) - } - - fs.writeFileSync(filePath, content, 'utf-8') -} diff --git a/lib/get-devland-file.js b/lib/get-devland-file.js new file mode 100644 index 0000000..670d57f --- /dev/null +++ b/lib/get-devland-file.js @@ -0,0 +1,7 @@ +module.exports = function getDevlandFile (name) { + return require( + require.resolve(name, { + paths: [ __dirname ] + }) + ) +} diff --git a/lib/loader.js.transform-quasar-imports.js b/lib/loader.js.transform-quasar-imports.js new file mode 100644 index 0000000..a279c0f --- /dev/null +++ b/lib/loader.js.transform-quasar-imports.js @@ -0,0 +1,37 @@ +const getDevlandFile = require('./get-devland-file') +const importMap = getDevlandFile('quasar/dist/transforms/import-map.json') + +const regex = /import\s*\{([\w,\s]+)\}\s*from\s*['"]{1}quasar['"]{1}/g + +function importTransformation (importName) { + const file = importMap[ importName ] + if (file === void 0) { + throw new Error('Unknown import from Quasar: ' + importName) + } + return 'quasar/' + file +} + +module.exports = function (content, map) { + const newContent = content.replace( + regex, + (_, match) => match.split(',') + .map(identifier => { + const id = identifier.trim() + + // might be an empty entry like below + // (notice useQuasar is followed by a comma) + // import { QTable, useQuasar, } from 'quasar' + if (id === '') { + return '' + } + + const data = id.split(' as ') + const name = data[0].trim() + + return `import ${data[1] !== void 0 ? data[1].trim() : name} from '${importTransformation(name)}';` + }) + .join('') + ) + + return this.callback(null, newContent, map) +} diff --git a/lib/loader.sass.js b/lib/loader.sass.js new file mode 100644 index 0000000..5d5ab58 --- /dev/null +++ b/lib/loader.sass.js @@ -0,0 +1,20 @@ + +const prefix = `@import 'quasar-variables', 'quasar/src/css/variables.sass'\n` + +module.exports = function (content) { + if (content.indexOf('$') !== -1) { + let useIndex = Math.max( + content.lastIndexOf('@use '), + content.lastIndexOf('@forward ') + ) + + if (useIndex === -1) { + return prefix + content + } + + const newLineIndex = content.indexOf('\n', useIndex) + 1 + return content.substr(0, newLineIndex) + prefix + content.substr(newLineIndex) + } + + return content +} diff --git a/lib/loader.scss.js b/lib/loader.scss.js new file mode 100644 index 0000000..a9689be --- /dev/null +++ b/lib/loader.scss.js @@ -0,0 +1,20 @@ + +const prefix = `@import 'quasar-variables', 'quasar/src/css/variables.sass';\n` + +module.exports = function (content) { + if (content.indexOf('$') !== -1) { + let useIndex = Math.max( + content.lastIndexOf('@use '), + content.lastIndexOf('@forward ') + ) + + if (useIndex === -1) { + return prefix + content + } + + const newLineIndex = content.indexOf('\n', useIndex) + 1 + return content.substr(0, newLineIndex) + prefix + content.substr(newLineIndex) + } + + return content +} diff --git a/lib/loader.vue.auto-import-quasar.js b/lib/loader.vue.auto-import-quasar.js new file mode 100644 index 0000000..fdb8225 --- /dev/null +++ b/lib/loader.vue.auto-import-quasar.js @@ -0,0 +1,102 @@ +const { getOptions } = require('loader-utils') + +const stringifyRequest = require('loader-utils/lib/stringifyRequest') +const getDevlandFile = require('./get-devland-file') + +const autoImportData = getDevlandFile('quasar/dist/transforms/auto-import.json') +const autoImportRuntimePath = require.resolve('./runtime.auto-import.js') +const importMap = getDevlandFile('quasar/dist/transforms/import-map.json') + +function importTransformation (importName) { + const file = importMap[ importName ] + if (file === void 0) { + throw new Error('Unknown import from Quasar: ' + importName) + } + return 'quasar/' + file +} + +const compRegex = { + 'kebab': new RegExp(autoImportData.regex.kebabComponents || autoImportData.regex.components, 'g'), + 'pascal': new RegExp(autoImportData.regex.pascalComponents || autoImportData.regex.components, 'g'), + 'combined': new RegExp(autoImportData.regex.components, 'g') +} + +const dirRegex = new RegExp(autoImportData.regex.directives, 'g') + +function transform (itemArray) { + return itemArray + .map(name => `import ${name} from '${importTransformation(name)}';`) + .join(`\n`) +} + +function extract (content, ctx, autoImportCase) { + let comp = content.match(compRegex[autoImportCase]) + let dir = content.match(dirRegex) + + if (comp === null && dir === null) { + return + } + + let importStatements = '' + let installStatements = '' + + if (comp !== null) { + // avoid duplicates + comp = Array.from(new Set(comp)) + + // map comp names only if not pascal-case already + if (autoImportCase !== 'pascal') { + comp = comp.map(name => autoImportData.importName[name]) + } + + if (autoImportCase === 'combined') { + // could have been transformed QIcon and q-icon too, + // so avoid duplicates + comp = Array.from(new Set(comp)) + } + + importStatements += transform(comp) + installStatements += `qInstall(script, 'components', {${comp.join(',')}});` + } + + if (dir !== null) { + dir = Array.from(new Set(dir)) + .map(name => autoImportData.importName[name]) + + importStatements += transform(dir) + installStatements += `qInstall(script, 'directives', {${dir.join(',')}});` + } + + // stringifyRequest needed so it doesn't + // messes up consistency of hashes between builds + return ` +${importStatements} +import qInstall from ${stringifyRequest(ctx, autoImportRuntimePath)}; +${installStatements} +` +} + +module.exports = function (content, map) { + let newContent = content + + if (!this.resourceQuery) { + const opts = getOptions(this) + + if (opts.isServerBuild !== true) { + const file = this.fs.readFileSync(this.resource, 'utf-8').toString() + const code = extract(file, this, opts.strategy) + + if (code !== void 0) { + const index = this.mode === 'development' + ? content.indexOf('/* hot reload */') + : -1 + + newContent = index === -1 + ? content + code + : content.slice(0, index) + code + content.slice(index) + } + } + } + + return this.callback(null, newContent, map) +} diff --git a/lib/runtime.auto-import.js b/lib/runtime.auto-import.js new file mode 100644 index 0000000..a665115 --- /dev/null +++ b/lib/runtime.auto-import.js @@ -0,0 +1,29 @@ +/** + * Quasar runtime for auto-importing + * components or directives. + * + * Warning! This file does NOT get transpiled by Babel + * but is included into the UI code. + * + * @param {component} Vue Component object + * @param {type} One of 'components' or 'directives' + * @param {items} Object containing components or directives + */ +module.exports = function qInstall (component, type, items) { + const targetComponent = component.__vccOpts !== void 0 + ? component.__vccOpts + : component + + const target = targetComponent[type] + + if (target === void 0) { + targetComponent[type] = items + } + else { + for (var i in items) { + if (target[i] === void 0) { + target[i] = items[i] + } + } + } +} diff --git a/package.json b/package.json index c4f2732..d63c3b2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-cli-plugin-quasar", - "version": "0.17.1", - "description": "Quasar Framework plugin for Vue CLI 3", + "version": "5.1.2", + "description": "Quasar Framework v2 plugin for Vue CLI v5", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -21,5 +21,9 @@ "bugs": { "url": "https://github.com/quasarframework/vue-cli-plugin-quasar/issues" }, - "homepage": "https://github.com/quasarframework/vue-cli-plugin-quasar#readme" + "homepage": "https://github.com/quasarframework/vue-cli-plugin-quasar#readme", + "dependencies": { + "loader-utils": "^1.4.0", + "webpack-merge": "^5.7.3" + } } diff --git a/prompts.js b/prompts.js index df24611..348b459 100644 --- a/prompts.js +++ b/prompts.js @@ -1,47 +1,61 @@ module.exports = [ { - name: 'quasar.theme', + name: 'quasar.replaceComponents', + type: 'confirm', + when: 'router', + message: + 'Allow Quasar to replace App.vue, About.vue, Home.vue and (if available) router.js?', + default: true + }, + + { + name: 'quasar.cssPreprocessor', type: 'list', - message: 'Select Quasar Theme:', + message: 'Pick your favorite CSS preprocessor:', + default: 'sass', choices: [ { - name: 'Material Design', - value: 'mat' + name: 'Sass with indented syntax', + value: 'sass', + short: 'Sass' }, { - name: 'iOS Theme', - value: 'ios' + name: 'Sass with SCSS syntax', + value: 'scss', + short: 'SCSS' + }, + { + name: `None (style variables won't be available)`, + value: 'none', + short: 'None' } ] }, - { - name: 'quasar.replaceComponents', - type: 'confirm', - when: 'router', - message: 'Allow Quasar to replace App.vue, About.vue, Home.vue and (if available) router.js?', - default: true - }, - { - name: 'quasar.all', - type: 'confirm', - message: 'Import all Quasar components, directives and plugins?', - default: false - }, - { - name: 'quasar.rtlSupport', - type: 'confirm', - message: 'Use RTL support?', - default: false - }, + { name: 'quasar.iconSet', type: 'list', - message: 'Choose Icon Set', + message: 'Choose Quasar Icon Set', choices: [ { name: 'Material Icons (recommended)', value: 'material-icons', - short: 'Material Icons' + short: 'Material' + }, + { + name: 'Material Icons Outlined', + value: 'material-icons-outlined', + short: 'Material Outlined' + }, + { + name: 'Material Icons Round', + value: 'material-icons-round', + short: 'Material Round' + }, + { + name: 'Material Icons Sharp', + value: 'material-icons-sharp', + short: 'Material Sharp' }, { name: 'Fontawesome', @@ -57,52 +71,79 @@ module.exports = [ name: 'MDI', value: 'mdi', short: 'MDI' + }, + { + name: 'Eva Icons', + value: 'eva-icons', + short: 'Eva' } ] }, + { - name: 'quasar.i18n', + name: 'quasar.lang', type: 'string', - message: 'Quasar i18n lang - one from https://github.com/quasarframework/quasar/tree/dev/i18n', - default: 'en-us', + message: + 'Default Quasar language pack - one from https://github.com/quasarframework/quasar/tree/dev/ui/lang', + default: 'en-US', validate: opt => opt && opt.length >= 2 }, + + { + name: 'quasar.rtlSupport', + type: 'confirm', + message: 'Use RTL support?', + default: false + }, + { name: 'quasar.features', type: 'checkbox', message: 'Select features:', choices: [ { - name: 'Animations', - value: 'animate' + name: 'Roboto font', + value: 'roboto-font' + }, + { + name: 'Material Icons (recommended)', + value: 'material-icons', + short: 'Material' }, { - name: 'IE11 support', - value: 'ie' + name: 'Material Icons Outlined', + value: 'material-icons-outlined', + short: 'Material Outlined' }, { - name: 'Roboto font', - value: 'roboto-font' + name: 'Material Icons Round', + value: 'material-icons-round', + short: 'Material Round' }, { - name: 'Material icons', - value: 'material-icons', - short: 'Material Icons' + name: 'Material Icons Sharp', + value: 'material-icons-sharp', + short: 'Material Sharp' }, { - name: 'Fontawesome icons', + name: 'Fontawesome', value: 'fontawesome', short: 'Fontawesome' }, { - name: 'Ionicons icons', + name: 'Ionicons', value: 'ionicons', short: 'Ionicons' }, { - name: 'MDI icons', + name: 'MDI', value: 'mdi', short: 'MDI' + }, + { + name: 'Eva Icons', + value: 'eva-icons', + short: 'Eva' } ] }