├── lib ├── shared-templates │ ├── gitkeep.template │ ├── npmrc.template │ ├── npmignore.template │ ├── editorconfig.template │ ├── travis.yml.template │ ├── eslintrc-test-override.template │ ├── gitignore.template │ └── appveyor.yml.template ├── core-generators │ ├── new │ │ ├── templates │ │ │ ├── assets │ │ │ │ ├── js │ │ │ │ │ ├── .gitkeep │ │ │ │ │ ├── pages │ │ │ │ │ │ ├── faq.page.js │ │ │ │ │ │ ├── legal │ │ │ │ │ │ │ ├── privacy.page.js │ │ │ │ │ │ │ └── terms.page.js │ │ │ │ │ │ ├── entrance │ │ │ │ │ │ │ ├── confirmed-email.page.js │ │ │ │ │ │ │ ├── forgot-password.page.js │ │ │ │ │ │ │ ├── new-password.page.js │ │ │ │ │ │ │ ├── login.page.js │ │ │ │ │ │ │ └── signup.page.js │ │ │ │ │ │ ├── contact.page.js │ │ │ │ │ │ ├── homepage.page.js │ │ │ │ │ │ ├── account │ │ │ │ │ │ │ ├── edit-password.page.js │ │ │ │ │ │ │ └── edit-profile.page.js │ │ │ │ │ │ └── dashboard │ │ │ │ │ │ │ └── welcome.page.js │ │ │ │ │ ├── cloud.setup.js │ │ │ │ │ └── components │ │ │ │ │ │ └── ajax-button.component.js │ │ │ │ ├── styles │ │ │ │ │ ├── pages │ │ │ │ │ │ ├── 404.less │ │ │ │ │ │ ├── 498.less │ │ │ │ │ │ ├── 500.less │ │ │ │ │ │ ├── contact.less │ │ │ │ │ │ ├── entrance │ │ │ │ │ │ │ ├── login.less │ │ │ │ │ │ │ ├── signup.less │ │ │ │ │ │ │ ├── new-password.less │ │ │ │ │ │ │ ├── confirmed-email.less │ │ │ │ │ │ │ └── forgot-password.less │ │ │ │ │ │ ├── legal │ │ │ │ │ │ │ ├── privacy.less │ │ │ │ │ │ │ └── terms.less │ │ │ │ │ │ ├── dashboard │ │ │ │ │ │ │ └── welcome.less │ │ │ │ │ │ ├── account │ │ │ │ │ │ │ ├── edit-password.less │ │ │ │ │ │ │ ├── edit-profile.less │ │ │ │ │ │ │ └── account-overview.less │ │ │ │ │ │ ├── faq.less │ │ │ │ │ │ └── homepage.less │ │ │ │ │ ├── mixins-and-variables │ │ │ │ │ │ ├── truncate.less │ │ │ │ │ │ ├── typography.less │ │ │ │ │ │ ├── index.less │ │ │ │ │ │ ├── containers.less │ │ │ │ │ │ ├── buttons.less │ │ │ │ │ │ └── colors.less │ │ │ │ │ ├── components │ │ │ │ │ │ ├── cloud-error.component.less │ │ │ │ │ │ ├── ajax-button.component.less │ │ │ │ │ │ ├── stripe-card-element.component.less │ │ │ │ │ │ └── modal.component.less │ │ │ │ │ ├── bootstrap-overrides.less │ │ │ │ │ ├── layout.less │ │ │ │ │ └── importer.less.template │ │ │ │ ├── favicon.ico │ │ │ │ ├── images │ │ │ │ │ ├── logo.png │ │ │ │ │ ├── hero-sky.png │ │ │ │ │ ├── hero-cloud.png │ │ │ │ │ ├── hero-image.png │ │ │ │ │ ├── hero-ship.png │ │ │ │ │ ├── hero-water.png │ │ │ │ │ ├── icon-close.png │ │ │ │ │ ├── setup-email.png │ │ │ │ │ ├── setup-payment.png │ │ │ │ │ └── setup-customize.png │ │ │ │ └── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── config │ │ │ │ ├── locales │ │ │ │ │ ├── de.json │ │ │ │ │ ├── en.json │ │ │ │ │ ├── es.json │ │ │ │ │ └── fr.json │ │ │ │ ├── local.js │ │ │ │ ├── policies.js.template │ │ │ │ ├── log.js │ │ │ │ ├── blueprints.js │ │ │ │ ├── session.js │ │ │ │ ├── views.js │ │ │ │ ├── i18n.js │ │ │ │ └── http.js │ │ │ ├── api │ │ │ │ ├── controllers │ │ │ │ │ ├── view-faq.js │ │ │ │ │ ├── legal │ │ │ │ │ │ ├── view-terms.js │ │ │ │ │ │ └── view-privacy.js │ │ │ │ │ ├── view-contact.js.template │ │ │ │ │ ├── account │ │ │ │ │ │ ├── view-edit-password.js.template │ │ │ │ │ │ ├── view-edit-profile.js.template │ │ │ │ │ │ ├── view-account-overview.js.template │ │ │ │ │ │ ├── update-password.js.template │ │ │ │ │ │ └── logout.js.template │ │ │ │ │ ├── entrance │ │ │ │ │ │ ├── view-confirmed-email.js.template │ │ │ │ │ │ ├── view-login.js.template │ │ │ │ │ │ ├── view-signup.js.template │ │ │ │ │ │ ├── view-forgot-password.js.template │ │ │ │ │ │ ├── view-new-password.js.template │ │ │ │ │ │ ├── send-password-recovery-email.js.template │ │ │ │ │ │ └── update-password-and-login.js.template │ │ │ │ │ ├── dashboard │ │ │ │ │ │ └── view-welcome.js │ │ │ │ │ ├── view-homepage-or-redirect.js.template │ │ │ │ │ ├── observe-my-session.js │ │ │ │ │ └── deliver-contact-form-message.js.template │ │ │ │ ├── helpers │ │ │ │ │ ├── redact-user.js.template │ │ │ │ │ └── broadcast-session-change.js.template │ │ │ │ ├── policies │ │ │ │ │ ├── is-super-admin.js │ │ │ │ │ └── is-logged-in.js │ │ │ │ └── responses │ │ │ │ │ ├── expired.js │ │ │ │ │ └── unauthorized.js │ │ │ ├── tasks │ │ │ │ ├── register │ │ │ │ │ ├── syncAssets.js │ │ │ │ │ ├── linkAssets.js │ │ │ │ │ ├── compileAssets.js │ │ │ │ │ ├── linkAssetsBuild.js │ │ │ │ │ ├── linkAssetsBuildProd.js │ │ │ │ │ ├── build.js │ │ │ │ │ ├── prod.js │ │ │ │ │ ├── buildProd.js │ │ │ │ │ ├── polyfill.js.template │ │ │ │ │ └── default.js.template │ │ │ │ └── config │ │ │ │ │ ├── sync.js │ │ │ │ │ ├── less.js │ │ │ │ │ ├── clean.js │ │ │ │ │ ├── cssmin.js │ │ │ │ │ ├── babel.js │ │ │ │ │ ├── concat.js │ │ │ │ │ ├── copy.js │ │ │ │ │ ├── uglify.js │ │ │ │ │ ├── hash.js │ │ │ │ │ └── watch.js.template │ │ │ ├── views │ │ │ │ ├── 404-caviar.ejs │ │ │ │ ├── 500-caviar.ejs │ │ │ │ ├── pages │ │ │ │ │ ├── entrance │ │ │ │ │ │ ├── confirmed-email.ejs │ │ │ │ │ │ ├── new-password.ejs │ │ │ │ │ │ ├── forgot-password.ejs │ │ │ │ │ │ └── login.ejs │ │ │ │ │ └── account │ │ │ │ │ │ ├── edit-password.ejs │ │ │ │ │ │ └── edit-profile.ejs │ │ │ │ ├── 498.ejs │ │ │ │ ├── eslintrc-override-for-inline-script-tags.template │ │ │ │ ├── emails │ │ │ │ │ ├── email-verify-account.ejs │ │ │ │ │ ├── email-verify-new-email.ejs │ │ │ │ │ ├── email-reset-password.ejs │ │ │ │ │ └── internal │ │ │ │ │ │ └── email-contact-form.ejs │ │ │ │ ├── gruntless-layout.ejs │ │ │ │ └── layout-email.ejs │ │ │ ├── Gruntfile.js.template │ │ │ ├── editorconfig.template │ │ │ ├── eslintignore.template │ │ │ ├── README.md.template │ │ │ ├── htmlhintrc.template │ │ │ ├── app.js.template │ │ │ └── lesshintrc.template │ │ └── example-test-folder.zip │ ├── page │ │ └── templates │ │ │ ├── stylesheet.less.template │ │ │ └── page-script.page.js.template │ ├── api │ │ └── index.js │ ├── model │ │ └── templates │ │ │ ├── attribute.template │ │ │ └── model.template │ ├── hook │ │ └── templates │ │ │ ├── package.json.template │ │ │ ├── index.js.template │ │ │ └── README.md.template │ ├── action │ │ └── templates │ │ │ ├── action.template │ │ │ └── actions2.template │ ├── generator │ │ ├── index.js │ │ ├── templates │ │ │ └── package.json.template │ │ └── before.js │ ├── controller │ │ └── templates │ │ │ ├── controller.template │ │ │ └── embedded-action.template │ ├── adapter │ │ ├── templates │ │ │ └── package.json.template │ │ └── index.js │ ├── email │ │ └── templates │ │ │ └── email.ejs │ ├── parasails │ │ └── index.js │ ├── etc │ │ └── index.js │ ├── script │ │ └── templates │ │ │ └── script.template │ ├── response │ │ ├── templates │ │ │ └── response.template │ │ └── index.js │ └── sails.io.js │ │ └── index.js ├── IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT.js ├── path-to-regexp.js └── builtins │ ├── jsonfile │ └── index.js │ ├── folder │ └── index.js │ ├── copy │ └── index.js │ └── file │ └── index.js ├── test ├── mocha.opts ├── generator.test.js ├── .eslintrc ├── new.test.js ├── unit │ ├── util │ │ ├── run-before-and-after.js │ │ └── expect-handler.js │ └── generate.jsonfile.test.js └── usage.test.js ├── .npmignore ├── .editorconfig ├── .travis.yml ├── .gitignore ├── package.json ├── README.md └── appveyor.yml /lib/shared-templates/gitkeep.template: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | -R dot 2 | --recursive 3 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/404.less: -------------------------------------------------------------------------------- 1 | [id='404'] { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/498.less: -------------------------------------------------------------------------------- 1 | [id='498'] { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/500.less: -------------------------------------------------------------------------------- 1 | [id='500'] { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/contact.less: -------------------------------------------------------------------------------- 1 | #contact { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/page/templates/stylesheet.less.template: -------------------------------------------------------------------------------- 1 | #<%= stem %> { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/entrance/login.less: -------------------------------------------------------------------------------- 1 | #login { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/legal/privacy.less: -------------------------------------------------------------------------------- 1 | #privacy { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/legal/terms.less: -------------------------------------------------------------------------------- 1 | #terms { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/dashboard/welcome.less: -------------------------------------------------------------------------------- 1 | #welcome { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/entrance/signup.less: -------------------------------------------------------------------------------- 1 | #signup { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/account/edit-password.less: -------------------------------------------------------------------------------- 1 | #edit-password { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/account/edit-profile.less: -------------------------------------------------------------------------------- 1 | #edit-profile { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/entrance/new-password.less: -------------------------------------------------------------------------------- 1 | #new-password { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/entrance/confirmed-email.less: -------------------------------------------------------------------------------- 1 | #confirmed-email { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/entrance/forgot-password.less: -------------------------------------------------------------------------------- 1 | #forgot-password { 2 | 3 | //… 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Willkommen", 3 | "A brand new app.": "Eine neue App." 4 | } 5 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Welcome", 3 | "A brand new app.": "A brand new app." 4 | } 5 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Bienvenido", 3 | "A brand new app.": "Una nueva aplicación." 4 | } 5 | -------------------------------------------------------------------------------- /lib/core-generators/new/example-test-folder.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/example-test-folder.zip -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Bienvenue", 3 | "A brand new app.": "Une toute nouvelle application." 4 | } 5 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/favicon.ico -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/logo.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/hero-sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/hero-sky.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/hero-cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/hero-cloud.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/hero-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/hero-image.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/hero-ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/hero-ship.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/hero-water.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/hero-water.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/icon-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/icon-close.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/setup-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/setup-email.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/mixins-and-variables/truncate.less: -------------------------------------------------------------------------------- 1 | .truncate() { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | white-space: nowrap; 5 | } 6 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/setup-payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/setup-payment.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/images/setup-customize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/images/setup-customize.png -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balderdashy/sails-generate/HEAD/lib/core-generators/new/templates/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/mixins-and-variables/typography.less: -------------------------------------------------------------------------------- 1 | // Font families: 2 | @main-font: 'Lato', sans-serif; 3 | @header-font: 'Lato', sans-serif; 4 | 5 | // Font weights: 6 | @bold: 700; 7 | @normal: 400; 8 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/components/cloud-error.component.less: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * App-wide styles for our cloud-errors. 5 | */ 6 | 7 | [parasails-component='cloud-error'] { 8 | // ... 9 | } 10 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/account/account-overview.less: -------------------------------------------------------------------------------- 1 | #account-overview { 2 | 3 | [purpose='remove-button'] { 4 | color: @brand; 5 | &:hover { 6 | color: @text-normal; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/mixins-and-variables/index.less: -------------------------------------------------------------------------------- 1 | @import 'colors.less'; 2 | @import 'typography.less'; 3 | @import 'buttons.less'; 4 | @import 'animations.less'; 5 | @import 'truncate.less'; 6 | @import 'containers.less'; 7 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/faq.less: -------------------------------------------------------------------------------- 1 | #faq { 2 | 3 | @media (max-width: 500px) { 4 | code { 5 | word-break: break-all; 6 | } 7 | [purpose='placeholder'] { 8 | word-break: break-all; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/mixins-and-variables/containers.less: -------------------------------------------------------------------------------- 1 | .container-sm() { 2 | width: 100%; 3 | max-width: 450px; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } 7 | 8 | .container-md() { 9 | width: 100%; 10 | max-width: 650px; 11 | margin-left: auto; 12 | margin-right: auto; 13 | } 14 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/mixins-and-variables/buttons.less: -------------------------------------------------------------------------------- 1 | .btn-reset() { 2 | border-top: none; 3 | border-bottom: none; 4 | border-left: none; 5 | border-right: none; 6 | background: transparent; 7 | font-family: inherit; 8 | cursor: pointer; 9 | &:focus { 10 | border-image: none; 11 | outline: none; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/shared-templates/npmrc.template: -------------------------------------------------------------------------------- 1 | ###################### 2 | # ╔╗╔╔═╗╔╦╗┬─┐┌─┐ # 3 | # ║║║╠═╝║║║├┬┘│ # 4 | # o╝╚╝╩ ╩ ╩┴└─└─┘ # 5 | ###################### 6 | 7 | # Hide NPM log output unless it is related to an error of some kind: 8 | loglevel=error 9 | 10 | # Make "npm audit" an opt-in thing for subsequent installs within this app: 11 | audit=false 12 | -------------------------------------------------------------------------------- /lib/core-generators/api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sails-generate-api 3 | * 4 | * Usage: 5 | * `sails generate api ` 6 | * 7 | * @type {Dictionary} 8 | */ 9 | 10 | module.exports = { 11 | 12 | targets: { 13 | 14 | // Call out to both the model and controller generators. 15 | '.': [ 16 | 'model', 17 | 'controller' 18 | ] 19 | 20 | } 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /lib/IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT.js: -------------------------------------------------------------------------------- 1 | /** 2 | * IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT() 3 | * 4 | * @type {Boolean} 5 | */ 6 | 7 | var RX_NODE_MAJOR_DOT_MINOR = /^v([^.]+)\.([^.]+)\./; 8 | var major = +(process.version.match(RX_NODE_MAJOR_DOT_MINOR)[1]); 9 | var minor = +(process.version.match(RX_NODE_MAJOR_DOT_MINOR)[2]); 10 | module.exports = (major >= 8) || (major >= 7 && minor >= 9); 11 | 12 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/view-faq.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View faq', 5 | 6 | 7 | description: 'Display "FAQ" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/faq' 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | // Respond with view. 22 | return {}; 23 | 24 | } 25 | 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/mixins-and-variables/colors.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Color Variables 3 | */ 4 | 5 | @brand: #14acc2; 6 | 7 | @error: #B53A03; 8 | 9 | 10 | @text-normal: #000; 11 | @text-muted: lighten(@text-normal, 60%); 12 | 13 | @bg-lt-gray: #f1f1f1; 14 | @border-lt-gray: darken(@bg-lt-gray, 5%); 15 | @accent-lt-gray: darken(#fff, 5%); 16 | @accent-md-gray: darken(#fff, 25%); 17 | @accent-white: #fff; 18 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/legal/view-terms.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View terms', 5 | 6 | 7 | description: 'Display "Legal terms" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/legal/terms' 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | // All done. 22 | return; 23 | 24 | } 25 | 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/syncAssets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/syncAssets.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * For more information see: 7 | * https://sailsjs.com/anatomy/tasks/register/sync-assets.js 8 | * 9 | */ 10 | module.exports = function(grunt) { 11 | grunt.registerTask('syncAssets', [ 12 | 'less:dev', 13 | 'sync:dev', 14 | ]); 15 | }; 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | ./.gitignore 3 | ./.jshintrc 4 | ./.editorconfig 5 | ./.travis.yml 6 | ./appveyor.yml 7 | ./example 8 | ./examples 9 | ./test 10 | ./tests 11 | ./.github 12 | 13 | node_modules 14 | npm-debug.log 15 | .node_history 16 | *.swo 17 | *.swp 18 | *.swn 19 | *.swm 20 | *.seed 21 | *.log 22 | *.out 23 | *.pid 24 | lib-cov 25 | .DS_STORE 26 | *# 27 | *\# 28 | .\#* 29 | *~ 30 | .idea 31 | .netbeans 32 | nbproject 33 | .tmp 34 | dump.rdb 35 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/legal/view-privacy.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View privacy', 5 | 6 | 7 | description: 'Display "Privacy policy" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/legal/privacy' 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | // All done. 22 | return; 23 | 24 | } 25 | 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/view-contact.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View contact', 5 | 6 | 7 | description: 'Display "Contact" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/contact' 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | // Respond with view. 22 | return {}; 23 | 24 | } 25 | 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /lib/core-generators/model/templates/attribute.template: -------------------------------------------------------------------------------- 1 | <% 2 | /** 3 | * Boilerplate attribute def 4 | * 5 | * This is the template used when creating attributes def 6 | * for models generated through the CLI 7 | * e.g. `sails generate model foo attrName:attrType` 8 | * 9 | */ 10 | %> 11 | <%if (lang === 'js') {%> <%= name %>: { type: '<%= type %>' } 12 | <%} else if (lang === 'coffee'){ %> <%=name%>: 13 | type: '<%= type %>' 14 | <%}%> 15 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/account/view-edit-password.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View edit password', 5 | 6 | 7 | description: 'Display "Edit password" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/account/edit-password' 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | return {}; 22 | 23 | } 24 | 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/account/view-edit-profile.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View edit profile', 5 | 6 | 7 | description: 'Display "Edit profile" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/account/edit-profile', 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | return {}; 22 | 23 | } 24 | 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /test/generator.test.js: -------------------------------------------------------------------------------- 1 | // /** 2 | // * Module dependencies 3 | // */ 4 | 5 | // var generate = require('../lib'); 6 | 7 | 8 | 9 | // generate({ 10 | // targets: { 11 | // '.': 'generator' 12 | // } 13 | // }, { 14 | // generatorName: 'foobarbar' 15 | // }, function (err, output) { 16 | // if (err) { 17 | // console.log('Error:',err); 18 | // return; 19 | // } 20 | 21 | // // --• 22 | // console.log(output); 23 | 24 | // }); 25 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/linkAssets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/linkAssets.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * For more information see: 7 | * https://sailsjs.com/anatomy/tasks/register/link-assets.js 8 | * 9 | */ 10 | module.exports = function(grunt) { 11 | grunt.registerTask('linkAssets', [ 12 | 'sails-linker:devJs', 13 | 'sails-linker:devStyles', 14 | ]); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/compileAssets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/compileAssets.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * For more information see: 7 | * https://sailsjs.com/anatomy/tasks/register/compile-assets.js 8 | * 9 | */ 10 | module.exports = function(grunt) { 11 | grunt.registerTask('compileAssets', [ 12 | 'clean:dev', 13 | 'less:dev', 14 | 'copy:dev', 15 | ]); 16 | }; 17 | -------------------------------------------------------------------------------- /lib/shared-templates/npmignore.template: -------------------------------------------------------------------------------- 1 | .git 2 | ./.gitignore 3 | ./.jshintrc 4 | ./.editorconfig 5 | ./.travis.yml 6 | ./appveyor.yml 7 | ./example 8 | ./examples 9 | ./test 10 | ./tests 11 | ./.github 12 | 13 | node_modules 14 | npm-debug.log 15 | .node_history 16 | *.swo 17 | *.swp 18 | *.swn 19 | *.swm 20 | *.seed 21 | *.log 22 | *.out 23 | *.pid 24 | lib-cov 25 | .DS_STORE 26 | *# 27 | *\# 28 | .\#* 29 | *~ 30 | .idea 31 | .netbeans 32 | nbproject 33 | .tmp 34 | dump.rdb 35 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/404-caviar.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

404

4 |
5 |

The page you seek doesn't exist.

6 |
7 |

Think you're seeing this message in error?
 Please let us know.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/500-caviar.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Oh my.

4 |
5 |

We've encountered an unexpected error.

6 |
7 |

Need help with your account? Please let us know.

8 | Open a help request 9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/view-confirmed-email.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View confirmed email', 5 | 6 | 7 | description: 'Display "Confirmed email" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/entrance/confirmed-email' 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | // Respond with view. 22 | return {}; 23 | 24 | } 25 | 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/linkAssetsBuild.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/linkAssetsBuild.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * For more information see: 7 | * https://sailsjs.com/anatomy/tasks/register/link-assets-build.js 8 | * 9 | */ 10 | module.exports = function(grunt) { 11 | grunt.registerTask('linkAssetsBuild', [ 12 | 'sails-linker:devJsBuild', 13 | 'sails-linker:devStylesBuild', 14 | ]); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/linkAssetsBuildProd.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/linkAssetsBuildProd.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * For more information see: 7 | * https://sailsjs.com/anatomy/tasks/register/link-assets-build-prod.js 8 | * 9 | */ 10 | module.exports = function(grunt) { 11 | grunt.registerTask('linkAssetsBuildProd', [ 12 | 'sails-linker:prodJsBuild', 13 | 'sails-linker:prodStylesBuild', 14 | ]); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/dashboard/view-welcome.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View welcome page', 5 | 6 | 7 | description: 'Display the dashboard "Welcome" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/dashboard/welcome', 14 | description: 'Display the welcome page for authenticated users.' 15 | }, 16 | 17 | }, 18 | 19 | 20 | fn: async function () { 21 | 22 | return {}; 23 | 24 | } 25 | 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # ╔═╗╔╦╗╦╔╦╗╔═╗╦═╗┌─┐┌─┐┌┐┌┌─┐┬┌─┐ 2 | # ║╣ ║║║ ║ ║ ║╠╦╝│ │ ││││├┤ ││ ┬ 3 | # o╚═╝═╩╝╩ ╩ ╚═╝╩╚═└─┘└─┘┘└┘└ ┴└─┘ 4 | # 5 | # This file (`.editorconfig`) exists to help maintain consistent formatting 6 | # throughout this package, the Sails framework, and the Node-Machine project. 7 | # 8 | # To review what each of these options mean, see: 9 | # http://editorconfig.org/ 10 | root = true 11 | 12 | [*] 13 | indent_style = space 14 | indent_size = 2 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/local.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Local environment settings 3 | * 4 | * Use this file to specify configuration settings for use while developing 5 | * the app on your personal system. 6 | * 7 | * For more information, check out: 8 | * https://sailsjs.com/docs/concepts/configuration/the-local-js-file 9 | */ 10 | 11 | module.exports = { 12 | 13 | // Any configuration settings may be overridden below, whether it's built-in Sails 14 | // options or custom configuration specifically for your app (e.g. Stripe, Sendgrid, etc.) 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/pages/homepage.less: -------------------------------------------------------------------------------- 1 | #homepage { 2 | 3 | [purpose='cloud-1'] { 4 | .fly-fade(); 5 | opacity: 0; 6 | } 7 | [purpose='cloud-2'] { 8 | .fly-fade(); 9 | .animation-delay(3.5s); 10 | opacity: 0; 11 | } 12 | [purpose='ship'] { 13 | .skid(); 14 | } 15 | [purpose='more-info-text'] { 16 | .bob(); 17 | } 18 | [purpose='setup-step'] { 19 | padding-left: 240px; 20 | } 21 | 22 | @media (max-width: 991px) { 23 | [purpose='setup-step'] { 24 | padding-left: 0px; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/pages/entrance/confirmed-email.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Email confirmed

4 |
5 |
6 |

Congratulations, your account is now verified! You now have full access to your account.

7 |

To my dashboard

8 |
9 |
10 |
11 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 12 | -------------------------------------------------------------------------------- /lib/shared-templates/editorconfig.template: -------------------------------------------------------------------------------- 1 | # ╔═╗╔╦╗╦╔╦╗╔═╗╦═╗┌─┐┌─┐┌┐┌┌─┐┬┌─┐ 2 | # ║╣ ║║║ ║ ║ ║╠╦╝│ │ ││││├┤ ││ ┬ 3 | # o╚═╝═╩╝╩ ╩ ╚═╝╩╚═└─┘└─┘┘└┘└ ┴└─┘ 4 | # 5 | # This file (`.editorconfig`) exists to help maintain consistent formatting 6 | # throughout this package, the Sails framework, and the Node-Machine project. 7 | # 8 | # To review what each of these options mean, see: 9 | # http://editorconfig.org/ 10 | root = true 11 | 12 | [*] 13 | indent_style = space 14 | indent_size = 2 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | -------------------------------------------------------------------------------- /lib/core-generators/hook/templates/package.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%- id %>", 3 | "version": "0.0.0", 4 | "license": "<%= typeof license !== 'undefined' ? license : '' %>", 5 | "description": "A Sails JS hook.", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"OK. (No further tests specified- at least not yet.)\" && exit 0", 9 | "pretest": "npm run lint", 10 | "lint": "node ./node_modules/eslint/bin/eslint . --max-warnings=0" 11 | }, 12 | "dependencies": {}, 13 | "devDependencies": { 14 | "eslint": "3.19.0" 15 | }, 16 | "sails": { 17 | "isHook": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/core-generators/hook/templates/index.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * <%= id %> hook 3 | * 4 | * @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions, and/or initialization logic. 5 | * @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks 6 | */ 7 | 8 | module.exports = function <%= _.camelCase('define '+id+' hook') %>(sails) { 9 | 10 | return { 11 | 12 | /** 13 | * Runs when this Sails app loads/lifts. 14 | */ 15 | initialize: async function() { 16 | 17 | sails.log.info('Initializing custom hook (`<%= id %>`)'); 18 | 19 | } 20 | 21 | }; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/view-login.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View login', 5 | 6 | 7 | description: 'Display "Login" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/entrance/login', 14 | }, 15 | 16 | redirect: { 17 | description: 'The requesting user is already logged in.', 18 | responseType: 'redirect' 19 | } 20 | 21 | }, 22 | 23 | 24 | fn: async function () { 25 | 26 | if (this.req.me) { 27 | throw {redirect: '/'}; 28 | } 29 | 30 | return {}; 31 | 32 | } 33 | 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/view-signup.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View signup', 5 | 6 | 7 | description: 'Display "Signup" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/entrance/signup', 14 | }, 15 | 16 | redirect: { 17 | description: 'The requesting user is already logged in.', 18 | responseType: 'redirect' 19 | } 20 | 21 | }, 22 | 23 | 24 | fn: async function () { 25 | 26 | if (this.req.me) { 27 | throw {redirect: '/'}; 28 | } 29 | 30 | return {}; 31 | 32 | } 33 | 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/helpers/redact-user.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Redact user', 5 | 6 | 7 | description: 'Destructively remove properties from the provided User record to prepare it for publication.', 8 | 9 | 10 | sync: true, 11 | 12 | 13 | inputs: { 14 | 15 | user: { 16 | type: 'ref', 17 | readOnly: false 18 | } 19 | 20 | }, 21 | 22 | 23 | fn: function ({ user }) { 24 | for (let [attrName, attrDef] of Object.entries(User.attributes)) { 25 | if (attrDef.protect) { 26 | delete user[attrName]; 27 | }//fi 28 | }//∞ 29 | } 30 | 31 | 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/faq.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('faq', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | //… 7 | }, 8 | 9 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 10 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 11 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 12 | beforeMount: function() { 13 | //… 14 | }, 15 | mounted: async function(){ 16 | //… 17 | }, 18 | 19 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 20 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 21 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 22 | methods: { 23 | //… 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/legal/privacy.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('privacy', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | //… 7 | }, 8 | 9 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 10 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 11 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 12 | beforeMount: function() { 13 | //… 14 | }, 15 | mounted: async function(){ 16 | //… 17 | }, 18 | 19 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 20 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 21 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 22 | methods: { 23 | //… 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/legal/terms.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('terms', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | //… 7 | }, 8 | 9 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 10 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 11 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 12 | beforeMount: function() { 13 | //… 14 | }, 15 | mounted: async function(){ 16 | //… 17 | }, 18 | 19 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 20 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 21 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 22 | methods: { 23 | //… 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /lib/core-generators/page/templates/page-script.page.js.template: -------------------------------------------------------------------------------- 1 | parasails.registerPage('<%= stem %>', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | //… 7 | }, 8 | 9 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 10 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 11 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 12 | beforeMount: function() { 13 | //… 14 | }, 15 | mounted: async function() { 16 | //… 17 | }, 18 | 19 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 20 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 21 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 22 | methods: { 23 | //… 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/entrance/confirmed-email.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('confirmed-email', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | //… 7 | }, 8 | 9 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 10 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 11 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 12 | beforeMount: function() { 13 | //… 14 | }, 15 | mounted: async function(){ 16 | //… 17 | }, 18 | 19 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 20 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 21 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 22 | methods: { 23 | //… 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | // ╔═╗╔═╗╦ ╦╔╗╔╔╦╗┬─┐┌─┐ ┌─┐┬ ┬┌─┐┬─┐┬─┐┬┌┬┐┌─┐ 3 | // ║╣ ╚═╗║ ║║║║ ║ ├┬┘│ │ │└┐┌┘├┤ ├┬┘├┬┘│ ││├┤ 4 | // o╚═╝╚═╝╩═╝╩╝╚╝ ╩ ┴└─└─┘ └─┘ └┘ └─┘┴└─┴└─┴─┴┘└─┘ 5 | // ┌─ ┌─┐┌─┐┬─┐ ┌─┐┬ ┬┌┬┐┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┬┐ ┌┬┐┌─┐┌─┐┌┬┐┌─┐ ─┐ 6 | // │ ├┤ │ │├┬┘ ├─┤│ │ │ │ ││││├─┤ │ ├┤ ││ │ ├┤ └─┐ │ └─┐ │ 7 | // └─ └ └─┘┴└─ ┴ ┴└─┘ ┴ └─┘┴ ┴┴ ┴ ┴ └─┘─┴┘ ┴ └─┘└─┘ ┴ └─┘ ─┘ 8 | // > An .eslintrc configuration override for use in the `test/` directory. 9 | // 10 | // (See .eslintrc in the root directory of this package for more info.) 11 | 12 | "extends": [ 13 | "../.eslintrc" 14 | ], 15 | 16 | "env": { 17 | "mocha": true 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/account/view-account-overview.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View account overview', 5 | 6 | 7 | description: 'Display "Account Overview" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/account/account-overview', 14 | } 15 | 16 | }, 17 | 18 | 19 | fn: async function () { 20 | 21 | // If billing features are enabled, include our configured Stripe.js 22 | // public key in the view locals. Otherwise, leave it as undefined. 23 | return { 24 | stripePublishableKey: sails.config.custom.enableBillingFeatures? sails.config.custom.stripePublishableKey : undefined, 25 | }; 26 | 27 | } 28 | 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/498.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Sorry, that link is expired, or
 it has already been used.

4 |
5 |
6 |

Need further help accessing your account?
 Please contact support or request a new password reset.

7 | Reset password 8 |
9 |
10 |
11 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 12 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/account/update-password.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Update password', 5 | 6 | 7 | description: 'Update the password for the logged-in user.', 8 | 9 | 10 | inputs: { 11 | 12 | password: { 13 | description: 'The new, unencrypted password.', 14 | example: 'abc123v2', 15 | required: true 16 | } 17 | 18 | }, 19 | 20 | 21 | fn: async function ({password}) { 22 | 23 | // Hash the new password. 24 | var hashed = await sails.helpers.passwords.hashPassword(password); 25 | 26 | // Update the record for the logged-in user. 27 | await User.updateOne({ id: this.req.me.id }) 28 | .set({ 29 | password: hashed 30 | }); 31 | 32 | } 33 | 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/view-forgot-password.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View forgot password', 5 | 6 | 7 | description: 'Display "Forgot password" page.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | viewTemplatePath: 'pages/entrance/forgot-password', 14 | }, 15 | 16 | redirect: { 17 | description: 'The requesting user is already logged in.', 18 | extendedDescription: 'Logged-in users should change their password in "Account settings."', 19 | responseType: 'redirect', 20 | } 21 | 22 | }, 23 | 24 | 25 | fn: async function () { 26 | 27 | if (this.req.me) { 28 | throw {redirect: '/'}; 29 | } 30 | 31 | return {}; 32 | 33 | } 34 | 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/Gruntfile.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * Gruntfile 3 | * 4 | * This Node script is executed when you run `grunt`-- and also when 5 | * you run `sails lift` (provided the grunt hook is installed and 6 | * hasn't been disabled). 7 | * 8 | * WARNING: 9 | * Unless you know what you're doing, you shouldn't change this file. 10 | * Check out the `tasks/` directory instead. 11 | * 12 | * For more information see: 13 | * https://sailsjs.com/anatomy/Gruntfile.js 14 | */ 15 | module.exports = function(grunt) { 16 | 17 | var loadGruntTasks = require('sails-hook-grunt/accessible/load-grunt-tasks'); 18 | 19 | // Load Grunt task configurations (from `tasks/config/`) and Grunt 20 | // task registrations (from `tasks/register/`). 21 | loadGruntTasks(__dirname, grunt); 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/build.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * This Grunt tasklist will be executed if you run `sails www` or 7 | * `grunt build` in a development environment. 8 | * 9 | * For more information see: 10 | * https://sailsjs.com/anatomy/tasks/register/build.js 11 | * 12 | */ 13 | module.exports = function(grunt) { 14 | grunt.registerTask('build', [ 15 | // 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.) 16 | 'compileAssets', 17 | // 'babel', //« uncomment to ALSO transpile during development (for broader browser compat.) 18 | 'linkAssetsBuild', 19 | 'clean:build', 20 | 'copy:build' 21 | ]); 22 | }; 23 | -------------------------------------------------------------------------------- /test/new.test.js: -------------------------------------------------------------------------------- 1 | // /** 2 | // * Module dependencies 3 | // */ 4 | 5 | // var _ = require('lodash'); 6 | // var generate = require('../lib'); 7 | 8 | 9 | 10 | // var sails = require('sails'); 11 | 12 | 13 | // sails.load({ 14 | // loadHooks: ['moduleloader', 'userconfig'] 15 | // }, function(err) { 16 | // if (err) { 17 | // console.log('Error:',err); 18 | // return; 19 | // } 20 | 21 | 22 | // generate({ 23 | // targets: { 24 | // '.': 'new' 25 | // } 26 | // }, { 27 | // sails: sails, 28 | // rootPath: process.cwd() + '/.foo' 29 | // }, function (err, output) { 30 | // if (err) { 31 | // console.log('Error:', err); 32 | // return; 33 | // } 34 | 35 | // //--• 36 | // console.log(output); 37 | 38 | // });// 39 | 40 | // });// 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 2 | # ╔╦╗╦═╗╔═╗╦ ╦╦╔═╗ ┬ ┬┌┬┐┬ # 3 | # ║ ╠╦╝╠═╣╚╗╔╝║╚═╗ └┬┘││││ # 4 | # o ╩ ╩╚═╩ ╩ ╚╝ ╩╚═╝o ┴ ┴ ┴┴─┘ # 5 | # # 6 | # This file configures Travis CI. # 7 | # (i.e. how we run the tests... mainly) # 8 | # # 9 | # https://docs.travis-ci.com/user/customizing-the-build # 10 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 11 | 12 | language: node_js 13 | 14 | node_js: 15 | - "10" 16 | - "12" 17 | - "14" 18 | - "16" 19 | 20 | branches: 21 | only: 22 | - master 23 | 24 | notifications: 25 | email: 26 | - ci@sailsjs.com 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/view-homepage-or-redirect.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View homepage or redirect', 5 | 6 | 7 | description: 'Display or redirect to the appropriate homepage, depending on login status.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | statusCode: 200, 14 | description: 'Requesting user is a guest, so show the public landing page.', 15 | viewTemplatePath: 'pages/homepage' 16 | }, 17 | 18 | redirect: { 19 | responseType: 'redirect', 20 | description: 'Requesting user is logged in, so redirect to the internal welcome page.' 21 | }, 22 | 23 | }, 24 | 25 | 26 | fn: async function () { 27 | 28 | if (this.req.me) { 29 | throw {redirect:'/welcome'}; 30 | } 31 | 32 | return {}; 33 | 34 | } 35 | 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /lib/shared-templates/travis.yml.template: -------------------------------------------------------------------------------- 1 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 2 | # ╔╦╗╦═╗╔═╗╦ ╦╦╔═╗ ┬ ┬┌┬┐┬ # 3 | # ║ ╠╦╝╠═╣╚╗╔╝║╚═╗ └┬┘││││ # 4 | # o ╩ ╩╚═╩ ╩ ╚╝ ╩╚═╝o ┴ ┴ ┴┴─┘ # 5 | # # 6 | # This file configures Travis CI. # 7 | # (i.e. how we run the tests... mainly) # 8 | # # 9 | # https://docs.travis-ci.com/user/customizing-the-build # 10 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 11 | 12 | language: node_js 13 | 14 | node_js: 15 | - "6" 16 | - "8" 17 | - "10" 18 | - "node" 19 | 20 | branches: 21 | only: 22 | - master 23 | 24 | notifications: 25 | email: 26 | - ci@sailsjs.com 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/observe-my-session.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Observe my session', 5 | 6 | 7 | description: 'Subscribe to the logged-in user\'s session so that you receive socket broadcasts when logged out in another tab.', 8 | 9 | 10 | exits: { 11 | 12 | success: { 13 | description: 'The requesting socket is now subscribed to socket broadcasts about the logged-in user\'s session.', 14 | }, 15 | 16 | }, 17 | 18 | 19 | fn: async function ({}) { 20 | 21 | if (!this.req.isSocket) { 22 | throw new Error('This action is designed for use with the virtual request interpreter (over sockets, not traditional HTTP).'); 23 | } 24 | 25 | let roomName = `session${_.deburr(this.req.sessionID)}`; 26 | sails.sockets.join(this.req, roomName); 27 | 28 | 29 | } 30 | 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /lib/shared-templates/eslintrc-test-override.template: -------------------------------------------------------------------------------- 1 | { 2 | // ╔═╗╔═╗╦ ╦╔╗╔╔╦╗┬─┐┌─┐ ┌─┐┬ ┬┌─┐┬─┐┬─┐┬┌┬┐┌─┐ 3 | // ║╣ ╚═╗║ ║║║║ ║ ├┬┘│ │ │└┐┌┘├┤ ├┬┘├┬┘│ ││├┤ 4 | // o╚═╝╚═╝╩═╝╩╝╚╝ ╩ ┴└─└─┘ └─┘ └┘ └─┘┴└─┴└─┴─┴┘└─┘ 5 | // ┌─ ┌─┐┌─┐┬─┐ ┌─┐┬ ┬┌┬┐┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┬┐ ┌┬┐┌─┐┌─┐┌┬┐┌─┐ ─┐ 6 | // │ ├┤ │ │├┬┘ ├─┤│ │ │ │ ││││├─┤ │ ├┤ ││ │ ├┤ └─┐ │ └─┐ │ 7 | // └─ └ └─┘┴└─ ┴ ┴└─┘ ┴ └─┘┴ ┴┴ ┴ ┴ └─┘─┴┘ ┴ └─┘└─┘ ┴ └─┘ ─┘ 8 | // > An .eslintrc configuration override for use with the tests in this directory. 9 | // 10 | // (See .eslintrc in the root directory of this package for more info.) 11 | 12 | "extends": [ 13 | "../.eslintrc" 14 | ], 15 | 16 | "env": { 17 | "mocha": true 18 | }, 19 | 20 | "globals": { 21 | // "assert": true, 22 | // "util": true 23 | // …plus any other convenience globals exposed in test suites 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/prod.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * This Grunt tasklist will be executed instead of `default` when 7 | * your Sails app is lifted in a production environment (e.g. using 8 | * `NODE_ENV=production node app`). 9 | * 10 | * For more information see: 11 | * https://sailsjs.com/anatomy/tasks/register/prod.js 12 | * 13 | */ 14 | module.exports = function(grunt) { 15 | grunt.registerTask('prod', [ 16 | 'polyfill:prod', //« Remove this to skip transpilation in production (not recommended) 17 | 'compileAssets', 18 | 'babel', //« Remove this to skip transpilation in production (not recommended) 19 | 'concat', 20 | 'uglify', 21 | 'cssmin', 22 | 'sails-linker:prodJs', 23 | 'sails-linker:prodStyles', 24 | ]); 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/bootstrap-overrides.less: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is for overriding some default bootstrap styles. 3 | * 4 | * > NOTE THAT THIS FILE AFFECTS GLOBAL STYLES. 5 | */ 6 | 7 | // lesshint-disable 8 | * { 9 | box-sizing: border-box; 10 | } 11 | // lesshint-enable 12 | 13 | img { 14 | display: block; 15 | } 16 | 17 | // Get rid of weird background on s with button styles 18 | .btn, [type='button'] { 19 | -webkit-appearance: none; 20 | } 21 | 22 | // Custom link styles within bodies of text 23 | h1, h2, h3, h4, h5, h6, p, li, blockquote, label { 24 | >a:not(.btn), small >a:not(.btn) { 25 | color: @brand; 26 | border-bottom: 1px solid @text-normal; 27 | &:hover { 28 | text-decoration: none; 29 | color: @text-normal; 30 | } 31 | } 32 | } 33 | 34 | blockquote { 35 | border-left: 3px solid @border-lt-gray; 36 | padding-left: 20px; 37 | } 38 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/eslintrc-override-for-inline-script-tags.template: -------------------------------------------------------------------------------- 1 | { 2 | // ╔═╗╔═╗╦ ╦╔╗╔╔╦╗┬─┐┌─┐ ┌─┐┬ ┬┌─┐┬─┐┬─┐┬┌┬┐┌─┐ 3 | // ║╣ ╚═╗║ ║║║║ ║ ├┬┘│ │ │└┐┌┘├┤ ├┬┘├┬┘│ ││├┤ 4 | // o╚═╝╚═╝╩═╝╩╝╚╝ ╩ ┴└─└─┘ └─┘ └┘ └─┘┴└─┴└─┴─┴┘└─┘ 5 | // ┌─ ┌─┐┌─┐┬─┐ ┬┌┐┌┬ ┬┌┐┌┌─┐ ┌─┐┌─┐┬─┐┬┌─┐┌┬┐ ┌┬┐┌─┐┌─┐┌─┐ ─┐ 6 | // │ ├┤ │ │├┬┘ │││││ ││││├┤ └─┐│ ├┬┘│├─┘ │ │ ├─┤│ ┬└─┐ │ 7 | // └─ └ └─┘┴└─ ┴┘└┘┴─┘┴┘└┘└─┘ └─┘└─┘┴└─┴┴ ┴ ┴ ┴ ┴└─┘└─┘ ─┘ 8 | // > An .eslintrc configuration override for use in the `views/` directory. 9 | // 10 | // (This works just like assets/.eslintrc, with one minor addition) 11 | // 12 | // For more information see: 13 | // https://sailsjs.com/anatomy/views/.eslintrc 14 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 15 | "extends": [ 16 | "../assets/.eslintrc" 17 | ], 18 | "rules": { 19 | "eol-last": [0] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/core-generators/action/templates/action.template: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | // ... 6 | 7 | 8 | /** 9 | * <%= relPath %> 10 | * 11 | * <%= description || (friendlyName+'.') %><% if (verbose) {%> 12 | * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 13 | * @purpose :: Server-side action for handling incoming requests. 14 | * @help :: See https://sailsjs.com/docs/concepts/actions 15 | * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<% } %> 16 | */ 17 | module.exports = <%= IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'async function' : 'function' %> <%= _.last(actionName.split('.')) %>(req, res) { 18 | <% if (inferredViewTemplatePath) { %> 19 | return res.view(<%= util.inspect(inferredViewTemplatePath) %>); 20 | <% } else { %> 21 | sails.log.debug('TODO: implement'); 22 | return res.ok(); 23 | <% } %> 24 | }; 25 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/policies/is-super-admin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * is-super-admin 3 | * 4 | * A simple policy that blocks requests from non-super-admins. 5 | * 6 | * For more about how to use policies, see: 7 | * https://sailsjs.com/config/policies 8 | * https://sailsjs.com/docs/concepts/policies 9 | * https://sailsjs.com/docs/concepts/policies/access-control-and-permissions 10 | */ 11 | module.exports = async function (req, res, proceed) { 12 | 13 | // First, check whether the request comes from a logged-in user. 14 | // > For more about where `req.me` comes from, check out this app's 15 | // > custom hook (`api/hooks/custom/index.js`). 16 | if (!req.me) { 17 | return res.unauthorized(); 18 | }//• 19 | 20 | // Then check that this user is a "super admin". 21 | if (!req.me.isSuperAdmin) { 22 | return res.forbidden(); 23 | }//• 24 | 25 | // IWMIH, we've got ourselves a "super admin". 26 | return proceed(); 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/policies/is-logged-in.js: -------------------------------------------------------------------------------- 1 | /** 2 | * is-logged-in 3 | * 4 | * A simple policy that allows any request from an authenticated user. 5 | * 6 | * For more about how to use policies, see: 7 | * https://sailsjs.com/config/policies 8 | * https://sailsjs.com/docs/concepts/policies 9 | * https://sailsjs.com/docs/concepts/policies/access-control-and-permissions 10 | */ 11 | module.exports = async function (req, res, proceed) { 12 | 13 | // If `req.me` is set, then we know that this request originated 14 | // from a logged-in user. So we can safely proceed to the next policy-- 15 | // or, if this is the last policy, the relevant action. 16 | // > For more about where `req.me` comes from, check out this app's 17 | // > custom hook (`api/hooks/custom/index.js`). 18 | if (req.me) { 19 | return proceed(); 20 | } 21 | 22 | //--• 23 | // Otherwise, this request did not come from a logged-in user. 24 | return res.unauthorized(); 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /lib/core-generators/generator/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sails-generate-generator 3 | * 4 | * Usage: 5 | * `sails generate generator :type` 6 | * 7 | * @scope {String} type [required, type of generator to create] 8 | * 9 | * @type {Dictionary} 10 | */ 11 | module.exports = { 12 | 13 | templatesDirectory: require('path').resolve(__dirname,'./templates'), 14 | 15 | before: require('./before'), 16 | 17 | targets: { 18 | './index.js': { template: 'index.js.template' }, 19 | './README.md': { template: 'README.md' }, 20 | './.gitignore': { template: '../../../shared-templates/gitignore.template' }, 21 | './.editorconfig': { template: '../../../shared-templates/editorconfig.template' }, 22 | './.npmignore': { template: '../../../shared-templates/npmignore.template' }, 23 | './package.json': { template: 'package.json.template' }, 24 | './templates/.gitkeep': { template: '../../../shared-templates/gitkeep.template' }, 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/responses/expired.js: -------------------------------------------------------------------------------- 1 | /** 2 | * expired.js 3 | * 4 | * A custom response that content-negotiates the current request to either: 5 | * • serve an HTML error page about the specified token being invalid or expired 6 | * • or send back 498 (Token Expired/Invalid) with no response body. 7 | * 8 | * Example usage: 9 | * ``` 10 | * return res.expired(); 11 | * ``` 12 | * 13 | * Or with actions2: 14 | * ``` 15 | * exits: { 16 | * badToken: { 17 | * description: 'Provided token was expired, invalid, or already used up.', 18 | * responseType: 'expired' 19 | * } 20 | * } 21 | * ``` 22 | */ 23 | module.exports = function expired() { 24 | 25 | var req = this.req; 26 | var res = this.res; 27 | 28 | sails.log.verbose('Ran custom response: res.expired()'); 29 | 30 | if (req.wantsJSON) { 31 | return res.status(498).send('Token Expired/Invalid'); 32 | } 33 | else { 34 | return res.status(498).view('498'); 35 | } 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/components/ajax-button.component.less: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * 5 | * App-wide styles for our ajax buttons. 6 | */ 7 | 8 | [parasails-component='ajax-button'] { 9 | .button-loader, .button-loading { 10 | display: none; 11 | margin: auto; 12 | .loading-dot { 13 | opacity: 0; 14 | display: inline; 15 | .fade-in(); 16 | .animation-duration(1s); 17 | .animation-iteration-count(infinite); 18 | .animation-direction(linear); 19 | &.dot1 { 20 | .animation-delay(0.25s); 21 | } 22 | &.dot2 { 23 | .animation-delay(0.5s); 24 | } 25 | &.dot3 { 26 | .animation-delay(0.75s); 27 | } 28 | &.dot4 { 29 | .animation-delay(1s); 30 | } 31 | } 32 | } 33 | &.syncing { 34 | .button-loader, .button-loading { 35 | display: inline-block; 36 | } 37 | .button-text { 38 | display: none; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/core-generators/controller/templates/controller.template: -------------------------------------------------------------------------------- 1 | <% 2 | // ╔╦╗╔═╗╔═╗╔═╗╦ ╦╦ ╔╦╗ 3 | // ║║║╣ ╠╣ ╠═╣║ ║║ ║ 4 | // ═╩╝╚═╝╚ ╩ ╩╚═╝╩═╝╩ 5 | // ┌─ ┬┌─┐┬ ┬┌─┐┌─┐┌─┐┬─┐┬┌─┐┌┬┐ ─┐ 6 | // │─── │├─┤└┐┌┘├─┤└─┐│ ├┬┘│├─┘ │ ───│ 7 | // └─ └┘┴ ┴ └┘ ┴ ┴└─┘└─┘┴└─┴┴ ┴ ─┘ 8 | if (lang === 'js') { %>/** 9 | * <%= entity %> 10 | * 11 | * @description :: Server-side actions for handling incoming requests. 12 | * @help :: See https://sailsjs.com/docs/concepts/actions 13 | */ 14 | 15 | module.exports = { 16 | <%= actionFns %> 17 | 18 | }; 19 | <% } 20 | // ╔═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ╔═╗╔═╗╦═╗╦╔═╗╔╦╗ 21 | // ║ ║ ║╠╣ ╠╣ ║╣ ║╣ ╚═╗║ ╠╦╝║╠═╝ ║ 22 | // ╚═╝╚═╝╚ ╚ ╚═╝╚═╝ ╚═╝╚═╝╩╚═╩╩ ╩ 23 | else if (lang === 'coffee') { %> # <%= entity %> 24 | # 25 | # @description :: Server-side actions for handling incoming requests. 26 | # @help :: See https://sailsjs.com/docs/concepts/actions 27 | module.exports = 28 | <% if (actionFns.length > 0) { 29 | %><%= actionFns %><% } 30 | else { %> 31 | {} 32 | <% } %> 33 | <% } %> 34 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/editorconfig.template: -------------------------------------------------------------------------------- 1 | ################################################ 2 | # ╔═╗╔╦╗╦╔╦╗╔═╗╦═╗┌─┐┌─┐┌┐┌┌─┐┬┌─┐ 3 | # ║╣ ║║║ ║ ║ ║╠╦╝│ │ ││││├┤ ││ ┬ 4 | # o╚═╝═╩╝╩ ╩ ╚═╝╩╚═└─┘└─┘┘└┘└ ┴└─┘ 5 | # 6 | # > Formatting conventions for your Sails app. 7 | # 8 | # This file (`.editorconfig`) exists to help 9 | # maintain consistent formatting throughout the 10 | # files in your Sails app. 11 | # 12 | # For the sake of convention, the Sails team's 13 | # preferred settings are included here out of the 14 | # box. You can also change this file to fit your 15 | # team's preferences (for example, if all of the 16 | # developers on your team have a strong preference 17 | # for tabs over spaces), 18 | # 19 | # To review what each of these options mean, see: 20 | # http://editorconfig.org/ 21 | # 22 | ################################################ 23 | root = true 24 | 25 | [*] 26 | indent_style = space 27 | indent_size = 2 28 | end_of_line = lf 29 | charset = utf-8 30 | trim_trailing_whitespace = true 31 | insert_final_newline = true 32 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/helpers/broadcast-session-change.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Broadcast session change', 5 | 6 | 7 | description: 'Broadcast a socket notification indicating a change in login status.', 8 | 9 | 10 | inputs: { 11 | 12 | req: { 13 | type: 'ref', 14 | required: true, 15 | }, 16 | 17 | }, 18 | 19 | 20 | exits: { 21 | 22 | success: { 23 | description: 'All done.', 24 | }, 25 | 26 | }, 27 | 28 | 29 | fn: async function ({ req }) { 30 | 31 | // If there's no sessionID, we don't need to broadcase a message about the old session. 32 | if(!req.sessionID) { 33 | return; 34 | } 35 | 36 | let roomName = `session${_.deburr(req.sessionID)}`; 37 | let messageText = `You have signed out or signed into a different session in another tab or window. Reload the page to refresh your session.`; 38 | sails.sockets.broadcast(roomName, 'session', { notificationText: messageText }, req); 39 | 40 | 41 | } 42 | 43 | 44 | }; 45 | 46 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/buildProd.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/buildProd.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * This Grunt tasklist will be executed instead of `build` if you 7 | * run `sails www` in a production environment, e.g.: 8 | * `NODE_ENV=production sails www` 9 | * 10 | * For more information see: 11 | * https://sailsjs.com/anatomy/tasks/register/build-prod.js 12 | * 13 | */ 14 | module.exports = function(grunt) { 15 | grunt.registerTask('buildProd', [ 16 | 'polyfill:prod', //« Remove this to skip transpilation in production (not recommended) 17 | 'compileAssets', 18 | 'babel', //« Remove this to skip transpilation in production (not recommended) 19 | 'concat', 20 | 'uglify', 21 | 'cssmin', 22 | 'hash',//« Cache-busting 23 | 'copy:beforeLinkBuildProd',//« For prettier URLs after cache-busting 24 | 'linkAssetsBuildProd', 25 | 'clean:build', 26 | 'copy:build', 27 | 'clean:afterBuildProd' 28 | ]); 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /lib/core-generators/model/templates/model.template: -------------------------------------------------------------------------------- 1 | <% if (lang === 'js') { %>/** 2 | * <%= filename %> 3 | * 4 | * @description :: A model definition represents a database table/collection. 5 | * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | 12 | // ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗ 13 | // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ 14 | // ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝ 15 | <%= attributes %> 16 | 17 | // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ 18 | // ║╣ ║║║╠╩╗║╣ ║║╚═╗ 19 | // ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝ 20 | 21 | 22 | // ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 23 | // ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗ 24 | // ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝ 25 | 26 | }, 27 | 28 | }; 29 | <%} else if (lang === 'coffee'){ %> # <%= filename %> 30 | # 31 | # @description :: A model definition represents a database table/collection. 32 | # @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models 33 | 34 | module.exports = 35 | 36 | attributes: 37 | 38 | <%= attributes %> 39 | 40 | <%}%> 41 | -------------------------------------------------------------------------------- /lib/core-generators/generator/templates/package.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%=packageName%>", 3 | "version": "1.0.0", 4 | "description": "sails generate <%=generatorType %>", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo 'No further automated tests have been implemented for this generator yet.'", 8 | "pretest": "npm run lint", 9 | "lint": "node ./node_modules/eslint/bin/eslint . --max-warnings=0" 10 | }, 11 | "keywords": [ 12 | "<%=generatorType%>", 13 | "generator", 14 | "sails", 15 | "generate", 16 | "plugin" 17 | ], 18 | "author": "<%= author %>", 19 | "license": "<%= typeof license !== 'undefined' ? license : '' %>", 20 | "dependencies": { 21 | "@sailshq/lodash": "<%= typeof lodashSVR !== 'undefined' ? lodashSVR : '^3.10.3' %>" 22 | }, 23 | "devDependencies": { 24 | "eslint": "3.19.0" 25 | }, 26 | "repository": <%= JSON.stringify(repository) %>, 27 | "sailsGenerator": { 28 | "type": "<%= generatorType %>", 29 | "behavior": "Implements or overrides `sails generate <%= generatorType %>`", 30 | "sailsVersion": "<%= sailsSVR %>" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/responses/unauthorized.js: -------------------------------------------------------------------------------- 1 | /** 2 | * unauthorized.js 3 | * 4 | * A custom response that content-negotiates the current request to either: 5 | * • log out the current user and redirect them to the login page 6 | * • or send back 401 (Unauthorized) with no response body. 7 | * 8 | * Example usage: 9 | * ``` 10 | * return res.unauthorized(); 11 | * ``` 12 | * 13 | * Or with actions2: 14 | * ``` 15 | * exits: { 16 | * badCombo: { 17 | * description: 'That email address and password combination is not recognized.', 18 | * responseType: 'unauthorized' 19 | * } 20 | * } 21 | * ``` 22 | */ 23 | module.exports = function unauthorized() { 24 | 25 | var req = this.req; 26 | var res = this.res; 27 | 28 | sails.log.verbose('Ran custom response: res.unauthorized()'); 29 | 30 | if (req.wantsJSON) { 31 | return res.sendStatus(401); 32 | } 33 | // Or log them out (if necessary) and then redirect to the login page. 34 | else { 35 | 36 | if (req.session.userId) { 37 | delete req.session.userId; 38 | } 39 | 40 | return res.redirect('/login'); 41 | } 42 | 43 | }; 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ┌─┐┬┌┬┐╦╔═╗╔╗╔╔═╗╦═╗╔═╗ 2 | # │ ┬│ │ ║║ ╦║║║║ ║╠╦╝║╣ 3 | # o└─┘┴ ┴ ╩╚═╝╝╚╝╚═╝╩╚═╚═╝ 4 | # 5 | # This file (`.gitignore`) exists to signify to `git` that certain files 6 | # and/or directories should be ignored for the purposes of version control. 7 | # 8 | # This is primarily useful for excluding temporary files of all sorts; stuff 9 | # generated by IDEs, build scripts, automated tests, package managers, or even 10 | # end-users (e.g. file uploads). `.gitignore` files like this also do a nice job 11 | # at keeping sensitive credentials and personal data out of version control systems. 12 | # 13 | 14 | ############################ 15 | # sails / node.js / npm 16 | ############################ 17 | node_modules 18 | .tmp 19 | npm-debug.log 20 | .waterline 21 | .node_history 22 | package-lock.json 23 | 24 | ############################ 25 | # editor & OS files 26 | ############################ 27 | *.swo 28 | *.swp 29 | *.swn 30 | *.swm 31 | *.seed 32 | *.log 33 | *.out 34 | *.pid 35 | lib-cov 36 | .DS_STORE 37 | *# 38 | *\# 39 | .\#* 40 | *~ 41 | .idea 42 | .netbeans 43 | nbproject 44 | 45 | ############################ 46 | # misc 47 | ############################ 48 | dump.rdb 49 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/emails/email-verify-account.ejs: -------------------------------------------------------------------------------- 1 | <% /* Note: This is injected into `views/layouts/layout-email.ejs` */ %> 2 |

Welcome, <%= fullName %>!

3 |

You're almost ready to get started. Just click the button below to confirm the email address for your account:

4 |
5 | Confirm email 6 |
7 |

If you have any trouble, try pasting this link in your browser: <%= url.resolve(sails.config.custom.baseUrl,'/email/confirm')+'?token='+encodeURIComponent(token) %>

8 |

Sincerely,

9 |

The NEW_APP_NAME Team

10 | -------------------------------------------------------------------------------- /lib/core-generators/adapter/templates/package.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= packageName %>", 3 | "version": "1.0.0", 4 | "description": "A <%=adapterName%> adapter for Sails / Waterline", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test/run-standard-tests", 8 | "pretest": "npm run lint", 9 | "lint": "node ./node_modules/eslint/bin/eslint . --max-warnings=0" 10 | }, 11 | "repository": <%= JSON.stringify(repository) %>, 12 | "keywords": [ 13 | <%= JSON.stringify(adapterName) %>, 14 | "adapter", 15 | "sails", 16 | "sails.js", 17 | "waterline", 18 | "orm", 19 | "database-adapter" 20 | ], 21 | "author": "<%= author %>", 22 | "license": "<%= typeof license !== 'undefined' ? license : '' %>", 23 | "dependencies": { 24 | "@sailshq/lodash": "^3.10.2" 25 | }, 26 | "devDependencies": { 27 | "eslint": "3.19.0", 28 | "waterline-adapter-tests": <%= JSON.stringify(waterlineAdapterTestsSVR) %> 29 | }, 30 | "waterlineAdapter": { 31 | "type": <%= JSON.stringify(adapterName) %>, 32 | "interfaces": [ 33 | "semantic", 34 | "queryable", 35 | "associations" 36 | ], 37 | "features": [] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/emails/email-verify-new-email.ejs: -------------------------------------------------------------------------------- 1 | <% /* Note: This is injected into `views/layouts/layout-email.ejs` */ %> 2 |

Dear <%= fullName %>,

3 |

You recently requested an update to the email address for your account. To verify your new email, please click the button below:

4 |
5 | Confirm email 6 |
7 |

If you have any trouble, try pasting this link in your browser: <%= url.resolve(sails.config.custom.baseUrl,'/email/confirm')+'?token='+encodeURIComponent(token) %>

8 |

Sincerely,

9 |

The NEW_APP_NAME Team

10 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/emails/email-reset-password.ejs: -------------------------------------------------------------------------------- 1 | <% /* Note: This is injected into `views/layouts/layout-email.ejs` */ %> 2 |

Dear <%= fullName %>,

3 |

Someone requested a password reset for your account. If this was not you, please disregard this email. Otherwise, simply click the button below:

4 |
5 | Update password 6 |
7 |

If you have any trouble, try pasting this link in your browser: <%= url.resolve(sails.config.custom.baseUrl,'/password/new')+'?token='+encodeURIComponent(token) %>

8 |

Sincerely,

9 |

The NEW_APP_NAME Team

10 | -------------------------------------------------------------------------------- /lib/core-generators/adapter/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sails-generate-adapter 3 | * 4 | * Usage: 5 | * `sails generate adapter` 6 | * 7 | * @type {Dictionary} 8 | */ 9 | 10 | module.exports = { 11 | 12 | templatesDirectory: require('path').resolve(__dirname,'./templates'), 13 | 14 | before: require('./before'), 15 | 16 | targets: { 17 | './:adapterPath': {folder: {}}, 18 | './:adapterPath/README.md': { template: 'README.md' }, 19 | './:adapterPath/.gitignore': { template: '../../../shared-templates/gitignore.template' }, 20 | './:adapterPath/.eslintrc': { template: '../../../shared-templates/eslintrc.template' }, 21 | './:adapterPath/.editorconfig': { template: '../../../shared-templates/editorconfig.template' }, 22 | './:adapterPath/.npmignore': { template: '../../../shared-templates/npmignore.template' }, 23 | './:adapterPath/package.json': { template: 'package.json.template' }, 24 | './:adapterPath/test/run-standard-tests.js': { template: 'test-templates/run-standard-tests.js' }, 25 | './:adapterPath/index.js': { template: 'index.js.template' } 26 | } 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/eslintignore.template: -------------------------------------------------------------------------------- 1 | <% /* BEWARE! Comments in this file can mess up some eslint parsers!!! */ 2 | if (verbose) {%>################################################ 3 | # ╔═╗╔═╗╦ ╦╔╗╔╔╦╗┬┌─┐┌┐┌┌─┐┬─┐┌─┐ 4 | # ║╣ ╚═╗║ ║║║║ ║ ││ ┬││││ │├┬┘├┤ 5 | # o╚═╝╚═╝╩═╝╩╝╚╝ ╩ ┴└─┘┘└┘└─┘┴└─└─┘ 6 | # 7 | # > Glob patterns indicating files/directories 8 | # > to exclude when checking code quality with 9 | # > eslint. 10 | # 11 | # This file (`.eslintignore`) is only relevant 12 | # if you are using eslint. 13 | # 14 | # It exists to signify to eslint that certain 15 | # files and/or directories should be ignored for 16 | # the purposes of linting -- e.g. because they 17 | # are already minified, or external dependencies, 18 | # contain inline JavaScript within an HTML template, 19 | # etc. 20 | # 21 | # Modify/extend/prune to fit your needs! 22 | # 23 | ################################################<% } 24 | 25 | if (typeof frontend === 'undefined' || frontend !== false) { %>assets/dependencies/**/*.js 26 | <% } 27 | 28 | if (!_.contains(without, 'views') && (typeof frontend === 'undefined' || frontend !== false)) { %>views/**/*.ejs<% if (verbose) { %> // (but see https://github.com/roadhump/SublimeLinter-eslint/issues/87)<% } %> 29 | <% } %> 30 | -------------------------------------------------------------------------------- /lib/shared-templates/gitignore.template: -------------------------------------------------------------------------------- 1 | # ┌─┐┬┌┬┐╦╔═╗╔╗╔╔═╗╦═╗╔═╗ 2 | # │ ┬│ │ ║║ ╦║║║║ ║╠╦╝║╣ 3 | # o└─┘┴ ┴ ╩╚═╝╝╚╝╚═╝╩╚═╚═╝ 4 | # 5 | # This file (`.gitignore`) exists to signify to `git` that certain files 6 | # and/or directories should be ignored for the purposes of version control. 7 | # 8 | # This is primarily useful for excluding temporary files of all sorts; stuff 9 | # generated by IDEs, build scripts, automated tests, package managers, or even 10 | # end-users (e.g. file uploads). `.gitignore` files like this also do a nice job 11 | # at keeping sensitive credentials and personal data out of version control systems. 12 | # 13 | 14 | ############################ 15 | # sails / node.js / npm 16 | ############################ 17 | node_modules 18 | .tmp 19 | npm-debug.log 20 | package-lock.json 21 | package-lock.* 22 | .waterline 23 | .node_history 24 | 25 | ############################ 26 | # editor & OS files 27 | ############################ 28 | *.swo 29 | *.swp 30 | *.swn 31 | *.swm 32 | *.seed 33 | *.log 34 | *.out 35 | *.pid 36 | lib-cov 37 | .DS_STORE 38 | *# 39 | *\# 40 | .\#* 41 | *~ 42 | .idea 43 | *.iml 44 | .netbeans 45 | nbproject 46 | .vscode 47 | 48 | ############################ 49 | # misc 50 | ############################ 51 | dump.rdb 52 | -------------------------------------------------------------------------------- /lib/core-generators/hook/templates/README.md.template: -------------------------------------------------------------------------------- 1 | # <%- id %> 2 | 3 | A [Sails.js](https://sailsjs.com) hook. 4 | 5 | ### Installation 6 | 7 | `npm install <%= id %> --save` 8 | 9 | ### Usage 10 | 11 | Usage info goes here... 12 | 13 | ### Configuration 14 | 15 | Parameter | Type | Default | Details 16 | -------------- | ------------------- | ------- | :---------------------------------: 17 | someConfigParam | ((string)) | `foobar` | Some configuration option users can set for this hook. 18 | 19 | #### Example 20 | 21 | An example use of this hook, if applicable: 22 | 23 | ```javascript 24 | sails.hooks['<%-hookName%>'].doAThing(); 25 | 26 | ``` 27 | 28 | ## Questions? 29 | 30 | See [Extending Sails > Hooks](https://sailsjs.com/docs/concepts/extending-sails/hooks) in the [Sails documentation](https://sailsjs.com/documentation), or check out [recommended support options](https://sailsjs.com/support). 31 | 32 | Sails.js logo (small) 33 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/policies.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * Policy Mappings 3 | * (sails.config.policies) 4 | * 5 | * Policies are simple functions which run **before** your actions. 6 | * 7 | * For more information on configuring policies, check out: 8 | * https://sailsjs.com/docs/concepts/policies 9 | */ 10 | 11 | module.exports.policies = { 12 | 13 | <% if (!caviar) { %> /*************************************************************************** 14 | * * 15 | * Default policy for all controllers and actions, unless overridden. * 16 | * (`true` allows public access) * 17 | * * 18 | ***************************************************************************/ 19 | 20 | // '*': true,<% 21 | } else { 22 | %> '*': 'is-logged-in', 23 | 24 | // Bypass the `is-logged-in` policy for: 25 | 'entrance/*': true, 26 | 'account/logout': true, 27 | 'view-homepage-or-redirect': true, 28 | 'view-faq': true, 29 | 'view-contact': true, 30 | 'legal/view-terms': true, 31 | 'legal/view-privacy': true, 32 | 'deliver-contact-form-message': true,<% 33 | } %> 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/emails/internal/email-contact-form.ejs: -------------------------------------------------------------------------------- 1 | <% /* Note: Unlike other emails, this is NOT intended for use with a layout! */ %> 2 |
3 |
4 | Sails 5 |
6 |
7 | 8 |

Dear admin,

9 |

We have received the following message through the contact form:

10 |

From: <%= contactName %> <<%= contactEmail %>>

11 |

Topic: <%= topic %>

12 |

Message: <%= message %>

13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /lib/core-generators/email/templates/email.ejs: -------------------------------------------------------------------------------- 1 | <% /* Note: This email is designed to be used within an email layout (e.g. `views/layouts/layout-email.ejs`) */ %> 2 |
3 |

TODO: Give this email a short title

4 |
5 |
6 |

Dear user,

7 |

TODO: Implement this email template.

8 |

Remember: Many email inbox clients only support inline styles-- no stylesheets! (If you didn't already know that, we're really, really sorry we had to be the ones to break it to you.)

9 |

If you're unsure about what to do next, get help here.

10 |

Finally, here's an example of a custom link somewhere on your server.

11 |
12 | -------------------------------------------------------------------------------- /test/unit/util/run-before-and-after.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var async = require('async'); 6 | var FileHeap = require('./FileHeap'); 7 | 8 | var writeJsonFile = require('../../../lib/builtins/jsonfile'); 9 | 10 | 11 | /** 12 | * Run `before` and `after` for Mocha. 13 | * This sets up the lifecycle needed by these tests. 14 | */ 15 | module.exports = function runBeforeAndAfter() { 16 | 17 | before(function(done) { 18 | var self = this; 19 | 20 | /* 21 | * Use an allocator to make it easier to manage files 22 | * generated during testing 23 | */ 24 | self.heap = new FileHeap(); 25 | 26 | /* 27 | * Another file allocator made to look like a Sails app 28 | * to test behavior with and without `--force`, inside 29 | * and outside of a Sails project directory. 30 | */ 31 | self.sailsHeap = new FileHeap({ 32 | path: '.tmp/someSailsApp/' 33 | }); 34 | writeJsonFile({ 35 | rootPath: self.sailsHeap.alloc('package.json'), 36 | data: { 37 | dependencies: { 38 | sails: '~99.9.99' 39 | } 40 | } 41 | }, { 42 | success: done 43 | }); 44 | 45 | }); 46 | 47 | 48 | 49 | after(function(done) { 50 | 51 | // Clean up loose files. 52 | async.parallel([ 53 | this.heap.cleanAll, 54 | this.sailsHeap.cleanAll, 55 | ], done); 56 | 57 | }); 58 | 59 | }; 60 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/entrance/forgot-password.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('forgot-password', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Main syncing/loading state for this page. 7 | syncing: false, 8 | 9 | // Form data 10 | formData: { /* … */ }, 11 | 12 | // For tracking client-side validation errors in our form. 13 | // > Has property set to `true` for each invalid property in `formData`. 14 | formErrors: { /* … */ }, 15 | 16 | // Form rules 17 | formRules: { 18 | emailAddress: {required: true, isEmail: true}, 19 | }, 20 | 21 | // Server error state for the form 22 | cloudError: '', 23 | 24 | // Success state when form has been submitted 25 | cloudSuccess: false, 26 | }, 27 | 28 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 29 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 30 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 31 | beforeMount: function() { 32 | //… 33 | }, 34 | mounted: async function() { 35 | //… 36 | }, 37 | 38 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 39 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 40 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 41 | methods: { 42 | 43 | submittedForm: async function() { 44 | // If it worked, show the success message. 45 | this.cloudSuccess = true; 46 | }, 47 | 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Built-in Log Configuration 3 | * (sails.config.log) 4 | * 5 | * Configure the log level for your app, as well as the transport 6 | * (Underneath the covers, Sails uses Winston for logging, which 7 | * allows for some pretty neat custom transports/adapters for log messages) 8 | * 9 | * For more information on the Sails logger, check out: 10 | * https://sailsjs.com/docs/concepts/logging 11 | */ 12 | 13 | module.exports.log = { 14 | 15 | /*************************************************************************** 16 | * * 17 | * Valid `level` configs: i.e. the minimum log level to capture with * 18 | * sails.log.*() * 19 | * * 20 | * The order of precedence for log levels from lowest to highest is: * 21 | * silly, verbose, info, debug, warn, error * 22 | * * 23 | * You may also set the level to "silent" to suppress all logs. * 24 | * * 25 | ***************************************************************************/ 26 | 27 | // level: 'info' 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sails-generate", 3 | "version": "2.0.13", 4 | "description": "Runner script for sails generators", 5 | "main": "lib/index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "@sailshq/lodash": "^3.10.3", 11 | "async": "2.6.4", 12 | "chalk": "2.3.0", 13 | "flaverr": "^1.0.0", 14 | "fs-extra": "0.30.0", 15 | "machinepack-process": "^4.0.0", 16 | "parasails": "^0.9.2", 17 | "read": "1.0.7", 18 | "reportback": "^2.0.1", 19 | "sails.io.js-dist": "^1.0.0" 20 | }, 21 | "devDependencies": { 22 | "checksum": "0.1.1", 23 | "eslint": "4.11.0", 24 | "mocha": "3.0.2" 25 | }, 26 | "scripts": { 27 | "test": "npm run lint && npm run custom-tests", 28 | "custom-tests": "node ./node_modules/mocha/bin/mocha -b", 29 | "lint": "node ./node_modules/eslint/bin/eslint . --max-warnings=0 --report-unused-disable-directives --ignore-pattern 'test/' --ignore-pattern 'templates/' && echo '✔ This code looks good.'" 30 | }, 31 | "keywords": [ 32 | "sails", 33 | "generate", 34 | "sails-generate", 35 | "generator", 36 | "automation" 37 | ], 38 | "author": "Mike McNeil", 39 | "repository": { 40 | "type": "git", 41 | "url": "git://github.com/balderdashy/sails-generate.git" 42 | }, 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "http://github.com/balderdashy/sails-generate/issues" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/usage.test.js: -------------------------------------------------------------------------------- 1 | // /** 2 | // * Module dependencies 3 | // */ 4 | 5 | // var _ = require('lodash'); 6 | // var generate = require('../lib'); 7 | 8 | 9 | 10 | // var scope = {}; 11 | 12 | // generate({ 13 | // targets: { 14 | // '.': { 15 | // exec: function (scope, cb) { 16 | // console.log('Test A'); 17 | // console.log('Current destPath :: '+scope.rootPath); 18 | // cb(); 19 | // } 20 | // }, 21 | 22 | // './erm': { 23 | // targets: { 24 | // '.': { 25 | // exec: function (scope, cb) { 26 | // console.log('Test B'); 27 | // console.log('Current destPath :: '+scope.rootPath); 28 | // cb(); 29 | // } 30 | // }, 31 | 32 | // 'evenDeeper': { 33 | // targets: { 34 | // '.': { 35 | // targets: { 36 | // '.': { 37 | // exec: function (scope, cb) { 38 | // console.log('Test C'); 39 | // console.log('Current destPath :: '+scope.rootPath); 40 | // cb(); 41 | // } 42 | // } 43 | // } 44 | // } 45 | // } 46 | // } 47 | // } 48 | // } 49 | // } 50 | // }, scope, function (err) { 51 | // if (err) { 52 | // console.log(err); 53 | // return; 54 | // } 55 | 56 | // console.log('Done.'); 57 | 58 | // }); 59 | -------------------------------------------------------------------------------- /lib/core-generators/parasails/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | 7 | 8 | /** 9 | * sails-generate-parasails 10 | * 11 | * (Re)generate parsails.js + Cloud SDK. 12 | * 13 | * Usage: 14 | * `sails generate parasails` 15 | * 16 | * @type {Dictionary} 17 | */ 18 | 19 | module.exports = { 20 | 21 | before: function(scope, done) { 22 | 23 | // This generator is intended to overwrite whatever files already exist at 24 | // these locations. 25 | scope.force = true; 26 | 27 | return done(); 28 | 29 | }, 30 | 31 | after: function(scope, done) { 32 | if (!scope.suppressFinalLog) { 33 | // Disable the "Created a new …!" output so we can use our own instead. 34 | scope.suppressFinalLog = true; 35 | 36 | console.log('Generated the latest recommended releases of parasails.js and cloud.js in `assets/dependencies/`.'); 37 | console.log(' [?] For help: https://sailsjs.com/support'); 38 | } 39 | 40 | return done(); 41 | }, 42 | 43 | // Set the templates directory to the `parasails` dependency's `dist/` folder. 44 | templatesDirectory: path.resolve(path.dirname(require.resolve('parasails')), 'dist/'), 45 | 46 | // ╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗ 47 | // ║ ╠═╣╠╦╝║ ╦║╣ ║ ╚═╗ 48 | // ╩ ╩ ╩╩╚═╚═╝╚═╝ ╩ ╚═╝ 49 | targets: { 50 | './assets/dependencies/parasails.js': { copy: 'parasails.js' }, 51 | './assets/dependencies/cloud.js': { copy: 'cloud.js' } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/gruntless-layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | New Sails App (not using Grunt) 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | 25 | 26 | <%- body %> 27 | 28 | 29 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/contact.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('contact', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Main syncing/loading state for this page. 7 | syncing: false, 8 | 9 | // Form data 10 | formData: { /* … */ }, 11 | 12 | // For tracking client-side validation errors in our form. 13 | // > Has property set to `true` for each invalid property in `formData`. 14 | formErrors: { /* … */ }, 15 | 16 | // Form rules 17 | formRules: { 18 | emailAddress: {isEmail: true, required: true}, 19 | fullName: {required: true}, 20 | topic: {required: true}, 21 | message: {required: true}, 22 | }, 23 | 24 | // Server error state for the form 25 | cloudError: '', 26 | 27 | // Success state when form has been submitted 28 | cloudSuccess: false, 29 | }, 30 | 31 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 32 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 33 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 34 | beforeMount: function() { 35 | //… 36 | }, 37 | mounted: async function() { 38 | //… 39 | }, 40 | 41 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 42 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 43 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 44 | methods: { 45 | 46 | submittedForm: async function() { 47 | 48 | // Show the success message. 49 | this.cloudSuccess = true; 50 | 51 | }, 52 | 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/homepage.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('homepage', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | //… 7 | }, 8 | 9 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 10 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 11 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 12 | beforeMount: function() { 13 | //… 14 | }, 15 | mounted: async function(){ 16 | this._setHeroHeight(); 17 | }, 18 | 19 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 20 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 21 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 22 | methods: { 23 | 24 | clickHeroButton: async function() { 25 | // Scroll to the 'get started' section: 26 | $('html, body').animate({ 27 | scrollTop: this.$find('[purpose="scroll-destination"]').offset().top 28 | }, 500); 29 | }, 30 | 31 | // Private methods not tied to a particular DOM event are prefixed with _ 32 | _setHeroHeight: function() { 33 | var $hero = this.$find('[purpose="full-page-hero"]'); 34 | var headerHeight = $('[purpose="page-header"]').outerHeight(); 35 | var heightToSet = $(window).height(); 36 | heightToSet = Math.max(heightToSet, 500);//« ensure min height of 500px - header height 37 | heightToSet = Math.min(heightToSet, 1000);//« ensure max height of 1000px - header height 38 | $hero.css('min-height', heightToSet - headerHeight+'px'); 39 | }, 40 | 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/sync.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/sync` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Synchronize files from the `assets` folder to `.tmp/public`, 7 | * smashing anything that's already there. 8 | * 9 | * For more information, see: 10 | * https://sailsjs.com/anatomy/tasks/config/sync.js 11 | * 12 | */ 13 | module.exports = function(grunt) { 14 | 15 | grunt.config.set('sync', { 16 | dev: { 17 | files: [{ 18 | cwd: './assets', 19 | src: ['**/*.!(coffee|less)'], 20 | dest: '.tmp/public' 21 | }] 22 | } 23 | }); 24 | 25 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 26 | // This Grunt plugin is part of the default asset pipeline in Sails, 27 | // so it's already been automatically loaded for you at this point. 28 | // 29 | // Of course, you can always remove this Grunt plugin altogether by 30 | // deleting this file. But check this out: you can also use your 31 | // _own_ custom version of this Grunt plugin. 32 | // 33 | // Here's how: 34 | // 35 | // 1. Install it as a local dependency of your Sails app: 36 | // ``` 37 | // $ npm install grunt-sync --save-dev --save-exact 38 | // ``` 39 | // 40 | // 41 | // 2. Then uncomment the following code: 42 | // 43 | // ``` 44 | // // Load Grunt plugin from the node_modules/ folder. 45 | // grunt.loadNpmTasks('grunt-sync'); 46 | // ``` 47 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/less.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/less` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Compile your LESS files into a CSS stylesheet. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/less.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('less', { 15 | dev: { 16 | files: [{ 17 | expand: true, 18 | cwd: 'assets/styles/', 19 | src: ['importer.less'], 20 | dest: '.tmp/public/styles/', 21 | ext: '.css' 22 | }] 23 | } 24 | }); 25 | 26 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 27 | // This Grunt plugin is part of the default asset pipeline in Sails, 28 | // so it's already been automatically loaded for you at this point. 29 | // 30 | // Of course, you can always remove this Grunt plugin altogether by 31 | // deleting this file. But check this out: you can also use your 32 | // _own_ custom version of this Grunt plugin. 33 | // 34 | // Here's how: 35 | // 36 | // 1. Install it as a local dependency of your Sails app: 37 | // ``` 38 | // $ npm install grunt-contrib-less --save-dev --save-exact 39 | // ``` 40 | // 41 | // 42 | // 2. Then uncomment the following code: 43 | // 44 | // ``` 45 | // // Load Grunt plugin from the node_modules/ folder. 46 | // grunt.loadNpmTasks('grunt-contrib-less'); 47 | // ``` 48 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 49 | 50 | }; 51 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/account/edit-password.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('edit-password', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Main syncing/loading state for this page. 7 | syncing: false, 8 | 9 | // Form data 10 | formData: { /* … */ }, 11 | 12 | // For tracking client-side validation errors in our form. 13 | // > Has property set to `true` for each invalid property in `formData`. 14 | formErrors: { /* … */ }, 15 | 16 | // Form rules 17 | formRules: { 18 | password: {required: true}, 19 | confirmPassword: {required: true, sameAs: 'password'}, 20 | }, 21 | 22 | // Server error state for the form 23 | cloudError: '', 24 | }, 25 | 26 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 27 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 28 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 29 | beforeMount: function() { 30 | //… 31 | }, 32 | mounted: async function() { 33 | //… 34 | }, 35 | 36 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 37 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 38 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 39 | methods: { 40 | 41 | submittedForm: async function() { 42 | // Redirect to a different web page on success. 43 | // > (Note that we re-enable the syncing state here. This is on purpose-- 44 | // > to make sure the spinner stays there until the page navigation finishes.) 45 | this.syncing = true; 46 | window.location = '/account'; 47 | }, 48 | 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /lib/path-to-regexp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * From Express core: (MIT License) 3 | * https://github.com/visionmedia/express/blob/cec0c06a70c874298761282984573475293c0071/lib/utils.js#L173 4 | * 5 | * -------------------------------------------------- 6 | * 7 | * Normalize the given path string, 8 | * returning a regular expression. 9 | * 10 | * An empty array should be passed, 11 | * which will contain the placeholder 12 | * key names. For example "/user/:id" will 13 | * then contain ["id"]. 14 | * 15 | * @param {String|RegExp|Array} path 16 | * @param {Array} keys 17 | * @param {Boolean} sensitive 18 | * @param {Boolean} strict 19 | * @return {RegExp} 20 | * @api private 21 | */ 22 | 23 | module.exports = function pathToRegexp(path, keys, sensitive, strict) { 24 | if (toString.call(path) === '[object RegExp]') {return path;} 25 | if (Array.isArray(path)) {path = '(' + path.join('|') + ')';} 26 | path = path 27 | .concat(strict ? '' : '/?') 28 | .replace(/\/\(/g, '(?:/') 29 | .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star) { 30 | keys.push({ 31 | name: key, 32 | optional: !! optional 33 | }); 34 | slash = slash || ''; 35 | return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + (optional || '') + (star ? '(/*)?' : ''); 36 | }) 37 | .replace(/([\/.])/g, '\\$1') 38 | .replace(/\*/g, '(.*)'); 39 | return new RegExp('^' + path + '$', sensitive ? '' : 'i'); 40 | }; 41 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/view-new-password.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'View new password', 5 | 6 | 7 | description: 'Display "New password" page.', 8 | 9 | 10 | inputs: { 11 | 12 | token: { 13 | description: 'The password reset token from the email.', 14 | example: '4-32fad81jdaf$329' 15 | } 16 | 17 | }, 18 | 19 | 20 | exits: { 21 | 22 | success: { 23 | viewTemplatePath: 'pages/entrance/new-password' 24 | }, 25 | 26 | invalidOrExpiredToken: { 27 | responseType: 'expired', 28 | description: 'The provided token is expired, invalid, or has already been used.', 29 | } 30 | 31 | }, 32 | 33 | 34 | fn: async function ({token}) { 35 | 36 | // If password reset token is missing, display an error page explaining that the link is bad. 37 | if (!token) { 38 | sails.log.warn('Attempting to view new password (recovery) page, but no reset password token included in request! Displaying error page...'); 39 | throw 'invalidOrExpiredToken'; 40 | }//• 41 | 42 | // Look up the user with this reset token. 43 | var userRecord = await User.findOne({ passwordResetToken: token }); 44 | // If no such user exists, or their token is expired, display an error page explaining that the link is bad. 45 | if (!userRecord || userRecord.passwordResetTokenExpiresAt <= Date.now()) { 46 | throw 'invalidOrExpiredToken'; 47 | } 48 | 49 | // Grab token and include it in view locals 50 | return { 51 | token, 52 | }; 53 | 54 | } 55 | 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/clean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/clean` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Remove generated files and folders. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/clean.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('clean', { 15 | dev: ['.tmp/public/**'], 16 | build: ['www'], 17 | afterBuildProd: [ 18 | 'www/concat', 19 | 'www/min', 20 | 'www/hash', 21 | 'www/js', 22 | 'www/styles', 23 | 'www/templates', 24 | 'www/dependencies' 25 | ] 26 | }); 27 | 28 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 29 | // This Grunt plugin is part of the default asset pipeline in Sails, 30 | // so it's already been automatically loaded for you at this point. 31 | // 32 | // Of course, you can always remove this Grunt plugin altogether by 33 | // deleting this file. But check this out: you can also use your 34 | // _own_ custom version of this Grunt plugin. 35 | // 36 | // Here's how: 37 | // 38 | // 1. Install it as a local dependency of your Sails app: 39 | // ``` 40 | // $ npm install grunt-contrib-clean --save-dev --save-exact 41 | // ``` 42 | // 43 | // 44 | // 2. Then uncomment the following code: 45 | // 46 | // ``` 47 | // // Load Grunt plugin from the node_modules/ folder. 48 | // grunt.loadNpmTasks('grunt-contrib-clean'); 49 | // ``` 50 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/entrance/new-password.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('new-password', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Main syncing/loading state for this page. 7 | syncing: false, 8 | 9 | // Form data 10 | formData: { /* … */ }, 11 | 12 | // For tracking client-side validation errors in our form. 13 | // > Has property set to `true` for each invalid property in `formData`. 14 | formErrors: { /* … */ }, 15 | 16 | // Form rules 17 | formRules: { 18 | password: {required: true}, 19 | confirmPassword: {required: true, sameAs: 'password'}, 20 | }, 21 | 22 | // Server error state for the form 23 | cloudError: '', 24 | }, 25 | 26 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 27 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 28 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 29 | beforeMount: function() { 30 | //… 31 | }, 32 | mounted: async function() { 33 | 34 | this.formData.token = this.token; 35 | 36 | }, 37 | 38 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 39 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 40 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 41 | methods: { 42 | 43 | submittedForm: async function() { 44 | // Redirect to the logged-in dashboard on success. 45 | // > (Note that we re-enable the syncing state here. This is on purpose-- 46 | // > to make sure the spinner stays there until the page navigation finishes.) 47 | this.syncing = true; 48 | window.location = '/'; 49 | }, 50 | 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sails-generate 2 | 3 | Master of ceremonies for generators in the Sails CLI. 4 | This is a built-in module within the Sails framework, used by the `sails` command-line interface. It is installed automatically when you run `npm install sails -g`. 5 | For customization options, see [Extending Sails > Generators](https://sailsjs.com/documentation/concepts/extending-sails/generators). 6 | 7 | 8 | ## Help 9 | 10 | If you have further questions or are having trouble, click [here](https://sailsjs.com/support). 11 | 12 | 13 | ## Bugs   [![NPM version](https://badge.fury.io/js/sails-generate.svg)](http://npmjs.com/package/sails-generate) 14 | 15 | To report a bug, [click here](https://sailsjs.com/bugs). 16 | 17 | 18 | ## Contributing 19 | 20 | Please observe the guidelines and conventions laid out in the [Sails project contribution guide](https://sailsjs.com/documentation/contributing) when opening issues or submitting pull requests. 21 | 22 | [![NPM](https://nodei.co/npm/sails-generate.png?downloads=true)](http://npmjs.com/package/sails-generate) 23 | 24 | 25 | #### What happened to the old core generators? 26 | 27 | Originally, Sails' core generators were spread across multiple repos and NPM packages. For easier maintainence, they were pulled inline in `sails-generate` as part of Sails v1. But they can still be overridden or disabled in [the same way as before](https://sailsjs.com/documentation/concepts/extending-sails/generators/custom-generators). 28 | 29 | ## License 30 | 31 | The [Sails framework](https://sailsjs.com) is free and open-source under the [MIT License](https://sailsjs.com/license). 32 | 33 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/cssmin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/cssmin` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Together with the `concat` task, this is the final step that minifies 7 | * all CSS files from `assets/styles/` (and potentially your LESS importer 8 | * file from `assets/styles/importer.less`) 9 | * 10 | * For more information, see: 11 | * https://sailsjs.com/anatomy/tasks/config/cssmin.js 12 | * 13 | */ 14 | module.exports = function(grunt) { 15 | 16 | grunt.config.set('cssmin', { 17 | dist: { 18 | src: ['.tmp/public/concat/production.css'], 19 | dest: '.tmp/public/min/production.min.css' 20 | } 21 | }); 22 | 23 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 24 | // This Grunt plugin is part of the default asset pipeline in Sails, 25 | // so it's already been automatically loaded for you at this point. 26 | // 27 | // Of course, you can always remove this Grunt plugin altogether by 28 | // deleting this file. But check this out: you can also use your 29 | // _own_ custom version of this Grunt plugin. 30 | // 31 | // Here's how: 32 | // 33 | // 1. Install it as a local dependency of your Sails app: 34 | // ``` 35 | // $ npm install grunt-contrib-cssmin --save-dev --save-exact 36 | // ``` 37 | // 38 | // 39 | // 2. Then uncomment the following code: 40 | // 41 | // ``` 42 | // // Load Grunt plugin from the node_modules/ folder. 43 | // grunt.loadNpmTasks('grunt-contrib-cssmin'); 44 | // ``` 45 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /lib/shared-templates/appveyor.yml.template: -------------------------------------------------------------------------------- 1 | # # # # # # # # # # # # # # # # # # # # # # # # # # 2 | # ╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╦╔═╗╦═╗ ┬ ┬┌┬┐┬ # 3 | # ╠═╣╠═╝╠═╝╚╗╔╝║╣ ╚╦╝║ ║╠╦╝ └┬┘││││ # 4 | # ╩ ╩╩ ╩ ╚╝ ╚═╝ ╩ ╚═╝╩╚═o ┴ ┴ ┴┴─┘ # 5 | # # 6 | # This file configures Appveyor CI. # 7 | # (i.e. how we run the tests on Windows) # 8 | # # 9 | # https://www.appveyor.com/docs/lang/nodejs-iojs/ # 10 | # # # # # # # # # # # # # # # # # # # # # # # # # # 11 | 12 | 13 | # Test against these versions of Node.js. 14 | environment: 15 | matrix: 16 | - nodejs_version: "6" 17 | - nodejs_version: "8" 18 | - nodejs_version: "10" 19 | 20 | # Install scripts. (runs after repo cloning) 21 | install: 22 | # Get the latest stable version of Node.js 23 | # (Not sure what this is for, it's just in Appveyor's example.) 24 | - ps: Install-Product node $env:nodejs_version 25 | # Install declared dependencies 26 | - npm install 27 | 28 | 29 | # Post-install test scripts. 30 | test_script: 31 | # Output Node and NPM version info. 32 | # (Presumably just in case Appveyor decides to try any funny business? 33 | # But seriously, always good to audit this kind of stuff for debugging.) 34 | - node --version 35 | - npm --version 36 | # Run the actual tests. 37 | - npm run custom-tests 38 | 39 | 40 | # Don't actually build. 41 | # (Not sure what this is for, it's just in Appveyor's example. 42 | # I'm not sure what we're not building... but I'm OK with not 43 | # building it. I guess.) 44 | build: off 45 | -------------------------------------------------------------------------------- /test/unit/util/expect-handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var _ = require('@sailshq/lodash'); 6 | 7 | 8 | /** 9 | * @param {Object||String} expectations 10 | * - if string specified, it is the name of the only valid handler 11 | * - if object specified, keys are handlers 12 | * - if value === true, this handler is allowed 13 | * - otherwise, use the value as an error 14 | */ 15 | 16 | module.exports = function expect(expectations) { 17 | var handlers = {}; 18 | if (typeof expectations === 'string') { 19 | handlers[expectations] = true; 20 | } else if (typeof expectations === 'object') { 21 | handlers = _.clone(expectations); 22 | } else {throw new Error('Invalid usage of `expect()` fixture in tests.');} 23 | 24 | return function(cb) { 25 | 26 | // Interpret handlers 27 | _.each(Object.keys(handlers), function(handlerName) { 28 | if (handlers[handlerName] === true) { 29 | handlers[handlerName] = function ignoreHandlerArgumentsBecauseItIsAlwaysGood() { 30 | cb(); 31 | }; 32 | } else { 33 | handlers[handlerName] = function incorrectHandlerFired(err) { 34 | var testMessage = 'Unexpected callback (' + handlerName + ') fired :: `' + expectations[handlerName] + '`'; 35 | 36 | if (err instanceof Error) { 37 | err.message = testMessage + (err.message ? ' :: ' + err.message : ''); 38 | } else {err = new Error(testMessage + (err ? ' :: ' + err : ''));} 39 | 40 | return cb(err); 41 | }; 42 | } 43 | }); 44 | 45 | // Trigger method 46 | this.fn(this.options, handlers); 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # # # # # # # # # # # # # # # # # # # # # # # # # # 2 | # ╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╦╔═╗╦═╗ ┬ ┬┌┬┐┬ # 3 | # ╠═╣╠═╝╠═╝╚╗╔╝║╣ ╚╦╝║ ║╠╦╝ └┬┘││││ # 4 | # ╩ ╩╩ ╩ ╚╝ ╚═╝ ╩ ╚═╝╩╚═o ┴ ┴ ┴┴─┘ # 5 | # # 6 | # This file configures Appveyor CI. # 7 | # (i.e. how we run the tests on Windows) # 8 | # # 9 | # https://www.appveyor.com/docs/lang/nodejs-iojs/ # 10 | # # # # # # # # # # # # # # # # # # # # # # # # # # 11 | 12 | 13 | # Test against these versions of Node.js. 14 | environment: 15 | matrix: 16 | - nodejs_version: "6" 17 | - nodejs_version: "8" 18 | - nodejs_version: "10" 19 | 20 | # Install scripts. (runs after repo cloning) 21 | install: 22 | # Get the latest stable version of Node.js 23 | # (Not sure what this is for, it's just in Appveyor's example.) 24 | - ps: Install-Product node $env:nodejs_version 25 | # Install declared dependencies 26 | - npm install 27 | 28 | 29 | # Post-install test scripts. 30 | test_script: 31 | # Output Node and NPM version info. 32 | # (Presumably just in case Appveyor decides to try any funny business? 33 | # But seriously, always good to audit this kind of stuff for debugging.) 34 | - node --version 35 | - npm --version 36 | # Run the actual tests. 37 | # (But note that we skip linting.) 38 | - npm run custom-tests 39 | 40 | 41 | # Don't actually build. 42 | # (Not sure what this is for, it's just in Appveyor's example. 43 | # I'm not sure what we're not building... but I'm OK with not 44 | # building it. I guess.) 45 | build: off 46 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/polyfill.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/polyfill.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * For more information see: 7 | * https://sailsjs.com/anatomy/tasks/register/polyfill.js 8 | * 9 | */ 10 | module.exports = function(grunt) { 11 | grunt.registerTask('polyfill:prod', 'Add the polyfill file to the top of the list of files to concatenate', <%= !IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'function() {':'()=>{' %> 12 | grunt.config.set('concat.js.src', [require('sails-hook-grunt/accessible/babel-polyfill')].concat(grunt.config.get('concat.js.src'))); 13 | }); 14 | grunt.registerTask('polyfill:dev', 'Add the polyfill file to the top of the list of files to copy and link', <%= !IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'function() {':'()=>{' %> 15 | grunt.config.set('copy.dev.files', grunt.config.get('copy.dev.files').concat({ 16 | expand: true, 17 | cwd: require('path').dirname(require('sails-hook-grunt/accessible/babel-polyfill')), 18 | src: require('path').basename(require('sails-hook-grunt/accessible/babel-polyfill')), 19 | dest: '.tmp/public/polyfill' 20 | })); 21 | var devLinkFiles = grunt.config.get('sails-linker.devJs.files'); 22 | grunt.config.set('sails-linker.devJs.files', Object.keys(devLinkFiles).reduce(<%= !IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'function(linkerConfigSoFar, glob) {':'(linkerConfigSoFar, glob)=>{' %> 23 | linkerConfigSoFar[glob] = ['.tmp/public/polyfill/polyfill.min.js'].concat(devLinkFiles[glob]); 24 | return linkerConfigSoFar; 25 | }, {})); 26 | }); 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/entrance/login.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('login', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Main syncing/loading state for this page. 7 | syncing: false, 8 | 9 | // Form data 10 | formData: { 11 | rememberMe: true, 12 | }, 13 | 14 | // For tracking client-side validation errors in our form. 15 | // > Has property set to `true` for each invalid property in `formData`. 16 | formErrors: { /* … */ }, 17 | 18 | // A set of validation rules for our form. 19 | // > The form will not be submitted if these are invalid. 20 | formRules: { 21 | emailAddress: { required: true, isEmail: true }, 22 | password: { required: true }, 23 | }, 24 | 25 | // Server error state for the form 26 | cloudError: '', 27 | }, 28 | 29 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 30 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 31 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 32 | beforeMount: function() { 33 | //… 34 | }, 35 | mounted: async function() { 36 | //… 37 | }, 38 | 39 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 40 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 41 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 42 | methods: { 43 | 44 | submittedForm: async function() { 45 | // Redirect to the logged-in dashboard on success. 46 | // > (Note that we re-enable the syncing state here. This is on purpose-- 47 | // > to make sure the spinner stays there until the page navigation finishes.) 48 | this.syncing = true; 49 | window.location = '/'; 50 | }, 51 | 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/babel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/babel` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Transpile >=ES6 code for broader browser compatibility. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/babel.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('babel', { 15 | dist: { 16 | options: { 17 | presets: [require('sails-hook-grunt/accessible/babel-preset-env')] 18 | }, 19 | files: [ 20 | { 21 | expand: true, 22 | cwd: '.tmp/public', 23 | src: ['js/**/*.js'], 24 | dest: '.tmp/public' 25 | } 26 | ] 27 | } 28 | }); 29 | 30 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 31 | // This Grunt plugin is part of the default asset pipeline in Sails, 32 | // so it's already been automatically loaded for you at this point. 33 | // 34 | // Of course, you can always remove this Grunt plugin altogether by 35 | // deleting this file. But check this out: you can also use your 36 | // _own_ custom version of this Grunt plugin. 37 | // 38 | // Here's how: 39 | // 40 | // 1. Install it as a local dependency of your Sails app: 41 | // ``` 42 | // $ npm install grunt-babel --save-dev --save-exact 43 | // ``` 44 | // 45 | // 46 | // 2. Then uncomment the following code: 47 | // 48 | // ``` 49 | // // Load Grunt plugin from the node_modules/ folder. 50 | // grunt.loadNpmTasks('grunt-babel'); 51 | // ``` 52 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 53 | 54 | }; 55 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/layout.less: -------------------------------------------------------------------------------- 1 | @footer-height: 40px; 2 | @container-md-max-width: 1100px; 3 | 4 | [v-cloak] { display: none; } 5 | 6 | html, body { 7 | height: 100%; 8 | margin: 0; 9 | } 10 | 11 | [purpose='page-wrap'] { 12 | height: 100%; 13 | /* lesshint-disable */height: auto !important;/* lesshint-enable */ 14 | // ^^The above is to disable "importantRule" and "duplicateProperty" rules. 15 | min-height: 100%; 16 | position: relative; 17 | padding-bottom: @footer-height; 18 | } 19 | 20 | [purpose='page-footer'] { 21 | border-top: 1px solid rgba(0, 0, 0, 0.1); 22 | height: @footer-height; 23 | width: 100%; 24 | position: absolute; 25 | left: 0px; 26 | bottom: 0px; 27 | } 28 | 29 | body.detected-mobile { 30 | // Above and beyond the media queries below, this selector (which relies on 31 | // `parasails` automatically attaching this class, if appropriate) contains 32 | // styles intended to be activated specifically when loaded from a recognized 33 | // mobile device, regardless of viewport dimensions. This includes tablet 34 | // devices (like the iPad) as well as handset devices (like the iPhone). 35 | // … 36 | } 37 | 38 | @media (max-width: 800px) { 39 | [purpose='page-wrap'] { 40 | padding-bottom: 75px; 41 | [purpose='page-footer'] { 42 | height: 75px; 43 | [purpose='footer-copy'], [purpose='footer-nav'] { 44 | width: 100%; 45 | display: block; 46 | text-align: center; 47 | } 48 | } 49 | } 50 | } 51 | 52 | @media (max-width: 575px) { 53 | [purpose='page-wrap'] { 54 | padding-bottom: 100px; 55 | [purpose='page-footer'] { 56 | height: 100px; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/concat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/concat` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * An intermediate step to generate monolithic files that can 7 | * then be passed in to `uglify` and/or `cssmin` for minification. 8 | * 9 | * For more information, see: 10 | * https://sailsjs.com/anatomy/tasks/config/concat.js 11 | * 12 | */ 13 | module.exports = function(grunt) { 14 | 15 | grunt.config.set('concat', { 16 | js: { 17 | src: require('../pipeline').jsFilesToInject, 18 | dest: '.tmp/public/concat/production.js' 19 | }, 20 | css: { 21 | src: require('../pipeline').cssFilesToInject, 22 | dest: '.tmp/public/concat/production.css' 23 | } 24 | }); 25 | 26 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 27 | // This Grunt plugin is part of the default asset pipeline in Sails, 28 | // so it's already been automatically loaded for you at this point. 29 | // 30 | // Of course, you can always remove this Grunt plugin altogether by 31 | // deleting this file. But check this out: you can also use your 32 | // _own_ custom version of this Grunt plugin. 33 | // 34 | // Here's how: 35 | // 36 | // 1. Install it as a local dependency of your Sails app: 37 | // ``` 38 | // $ npm install grunt-contrib-concat --save-dev --save-exact 39 | // ``` 40 | // 41 | // 42 | // 2. Then uncomment the following code: 43 | // 44 | // ``` 45 | // // Load Grunt plugin from the node_modules/ folder. 46 | // grunt.loadNpmTasks('grunt-contrib-concat'); 47 | // ``` 48 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 49 | 50 | }; 51 | -------------------------------------------------------------------------------- /lib/core-generators/etc/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | 7 | 8 | /** 9 | * Usage: 10 | * `sails generate etc` 11 | * 12 | * @type {Dictionary} 13 | */ 14 | 15 | module.exports = { 16 | 17 | templatesDirectory: path.resolve(__dirname,'./templates'), 18 | 19 | targets: { 20 | './.gitignore': { copy: '../../../shared-templates/gitignore.template' }, 21 | './.eslintrc': { template: '../../../shared-templates/eslintrc.template' }, 22 | './.editorconfig': { copy: '../../../shared-templates/editorconfig.template' }, 23 | './.npmignore': { copy: '../../../shared-templates/npmignore.template' }, 24 | './.travis.yml': { copy: '../../../shared-templates/travis.yml.template' }, 25 | './appveyor.yml': { copy: '../../../shared-templates/appveyor.yml.template' }, 26 | './test/.eslintrc': { copy: '../../../shared-templates/eslintrc-test-override.template' }, 27 | }, 28 | 29 | before: function (scope, done) { 30 | 31 | // Unless `force` was explicitly set to `false`, we'll set it to `true`. 32 | // (Most of the time, this generator is replacing pre-existing files.) 33 | if (scope.force !== false) { 34 | scope.force = true; 35 | } 36 | 37 | return done(); 38 | }, 39 | 40 | after: function (scope, done) { 41 | done.log.info('Replaced: `.gitignore`, `.editorconfig`, `.npmignore`, `.travis.yml`, & `appveyor.yml`.'); 42 | done.log.warn('Before you commit these changes:'); 43 | done.log.warn('Run `git diff` to make sure this didn\'t wipe out any important customizations!'); 44 | return done(); 45 | } 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/cloud.setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * cloud.setup.js 3 | * 4 | * Configuration for this Sails app's generated browser SDK ("Cloud"). 5 | * 6 | * Above all, the purpose of this file is to provide endpoint definitions, 7 | * each of which corresponds with one particular route+action on the server. 8 | * 9 | * > This file was automatically generated. 10 | * > (To regenerate, run `sails run rebuild-cloud-sdk`) 11 | */ 12 | 13 | Cloud.setup({ 14 | 15 | /* eslint-disable */ 16 | methods: {"confirmEmail":{"verb":"GET","url":"/email/confirm","args":["token"]},"logout":{"verb":"GET","url":"/api/v1/account/logout","args":[]},"updatePassword":{"verb":"PUT","url":"/api/v1/account/update-password","args":["password"]},"updateProfile":{"verb":"PUT","url":"/api/v1/account/update-profile","args":["fullName","emailAddress"]},"updateBillingCard":{"verb":"PUT","url":"/api/v1/account/update-billing-card","args":["stripeToken","billingCardLast4","billingCardBrand","billingCardExpMonth","billingCardExpYear"]},"login":{"verb":"PUT","url":"/api/v1/entrance/login","args":["emailAddress","password","rememberMe"]},"signup":{"verb":"POST","url":"/api/v1/entrance/signup","args":["emailAddress","password","fullName"]},"sendPasswordRecoveryEmail":{"verb":"POST","url":"/api/v1/entrance/send-password-recovery-email","args":["emailAddress"]},"updatePasswordAndLogin":{"verb":"POST","url":"/api/v1/entrance/update-password-and-login","args":["password","token"]},"deliverContactFormMessage":{"verb":"POST","url":"/api/v1/deliver-contact-form-message","args":["emailAddress","topic","fullName","message"]},"observeMySession":{"verb":"POST","url":"/api/v1/observe-my-session","args":[],"protocol":"io.socket"}} 17 | /* eslint-enable */ 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /lib/core-generators/controller/templates/embedded-action.template: -------------------------------------------------------------------------------- 1 | <% 2 | // ╔╦╗╔═╗╔═╗╔═╗╦ ╦╦ ╔╦╗ 3 | // ║║║╣ ╠╣ ╠═╣║ ║║ ║ 4 | // ═╩╝╚═╝╚ ╩ ╩╚═╝╩═╝╩ 5 | // ┌─ ┬┌─┐┬ ┬┌─┐┌─┐┌─┐┬─┐┬┌─┐┌┬┐ ─┐ 6 | // │─── │├─┤└┐┌┘├─┤└─┐│ ├┬┘│├─┘ │ ───│ 7 | // └─ └┘┴ ┴ └┘ ┴ ┴└─┘└─┘┴└─┴┴ ┴ ─┘ 8 | if (lang === 'js') { %> 9 | 10 | /** 11 | * <%= commentHeading %> 12 | */ 13 | <%= actionName %>: <%= IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'async function' : 'function' %> (req, res) {<% 14 | // Only display this notice in verbose mode 15 | if (verbose) { 16 | %> 17 | 18 | // NOTE: This function is in a Sails controller-- that means that not only do `req` and `res` 19 | // work just like their Express equivalents for HTTP requests, they also automatically listen to 20 | // and support the same interface for properly structured Socket.io messages. 21 | <% } %> 22 | return res.json({ 23 | todo: '<%=actionName%>() is not implemented yet!' 24 | }); 25 | } 26 | <% } 27 | // ╔═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ╔═╗╔═╗╦═╗╦╔═╗╔╦╗ 28 | // ║ ║ ║╠╣ ╠╣ ║╣ ║╣ ╚═╗║ ╠╦╝║╠═╝ ║ 29 | // ╚═╝╚═╝╚ ╚ ╚═╝╚═╝ ╚═╝╚═╝╩╚═╩╩ ╩ 30 | else if (lang === 'coffee') { %> 31 | 32 | # <%= commentHeading %> 33 | 34 | <%= actionName %>: (req, res) -><% 35 | // Only display this notice in verbose mode 36 | if (verbose) { 37 | %> 38 | # NOTE: This function is in a Sails controller-- that means that not only do `req` and `res` 39 | # work just like their Express equivalents for HTTP requests, they also automatically listen to 40 | # and support the same interface for properly structured Socket.io messages. 41 | <% } %> 42 | res.json 43 | todo: '<%=actionName%>() is not implemented yet!' 44 | <% } %> 45 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/account/edit-profile.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('edit-profile', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Main syncing/loading state for this page. 7 | syncing: false, 8 | 9 | // Form data 10 | formData: { /* … */ }, 11 | 12 | // For tracking client-side validation errors in our form. 13 | // > Has property set to `true` for each invalid property in `formData`. 14 | formErrors: { /* … */ }, 15 | 16 | // Form rules 17 | formRules: { 18 | fullName: {required: true}, 19 | emailAddress: {required: true, isEmail: true}, 20 | }, 21 | 22 | // Server error state for the form 23 | cloudError: '', 24 | }, 25 | 26 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 27 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 28 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 29 | beforeMount: function() { 30 | // Set the form data. 31 | this.formData.fullName = this.me.fullName; 32 | this.formData.emailAddress = this.me.emailChangeCandidate ? this.me.emailChangeCandidate : this.me.emailAddress; 33 | }, 34 | mounted: async function() { 35 | //… 36 | }, 37 | 38 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 39 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 40 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 41 | methods: { 42 | 43 | submittedForm: async function() { 44 | // Redirect to the account page on success. 45 | // > (Note that we re-enable the syncing state here. This is on purpose-- 46 | // > to make sure the spinner stays there until the page navigation finishes.) 47 | this.syncing = true; 48 | window.location = '/account'; 49 | }, 50 | 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/account/logout.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Logout', 5 | 6 | 7 | description: 'Log out of this app.', 8 | 9 | 10 | extendedDescription: 11 | `This action deletes the \`req.session.userId\` key from the session of the requesting user agent. 12 | Actual garbage collection of session data depends on this app's session store, and 13 | potentially also on the [TTL configuration](https://sailsjs.com/docs/reference/configuration/sails-config-session) 14 | you provided for it. 15 | 16 | Note that this action does not check to see whether or not the requesting user was 17 | actually logged in. (If they weren't, then this action is just a no-op.)`, 18 | 19 | 20 | exits: { 21 | 22 | success: { 23 | description: 'The requesting user agent has been successfully logged out.' 24 | }, 25 | 26 | redirect: { 27 | description: 'The requesting user agent looks to be a web browser.', 28 | extendedDescription: 'After logging out from a web browser, the user is redirected away.', 29 | responseType: 'redirect' 30 | } 31 | 32 | }, 33 | 34 | 35 | fn: async function () { 36 | 37 | // Clear the `userId` property from this session. 38 | delete this.req.session.userId; 39 | 40 | // Broadcast a message that we can display in other open tabs. 41 | if (sails.hooks.sockets) { 42 | await sails.helpers.broadcastSessionChange(this.req); 43 | } 44 | 45 | // Then finish up, sending an appropriate response. 46 | // > Under the covers, this persists the now-logged-out session back 47 | // > to the underlying session store. 48 | if (!this.req.wantsJSON) { 49 | throw {redirect: '/login'}; 50 | } 51 | 52 | } 53 | 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/dashboard/welcome.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('welcome', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | modal: '', 7 | pageLoadedAt: Date.now() 8 | }, 9 | 10 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 11 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 12 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 13 | beforeMount: function() { 14 | //… 15 | }, 16 | mounted: async function() { 17 | //… 18 | }, 19 | 20 | // ╦ ╦╦╦═╗╔╦╗╦ ╦╔═╗╦ ╔═╗╔═╗╔═╗╔═╗╔═╗ 21 | // ╚╗╔╝║╠╦╝ ║ ║ ║╠═╣║ ╠═╝╠═╣║ ╦║╣ ╚═╗ 22 | // ╚╝ ╩╩╚═ ╩ ╚═╝╩ ╩╩═╝ ╩ ╩ ╩╚═╝╚═╝╚═╝ 23 | // Configure deep-linking (aka client-side routing) 24 | virtualPagesRegExp: /^\/welcome\/?([^\/]+)?\/?/, 25 | afterNavigate: async function(virtualPageSlug){ 26 | // `virtualPageSlug` is determined by the regular expression above, which 27 | // corresponds with `:unused?` in the server-side route for this page. 28 | switch (virtualPageSlug) { 29 | case 'hello': 30 | this.modal = 'example'; 31 | break; 32 | default: 33 | this.modal = ''; 34 | } 35 | }, 36 | 37 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 38 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 39 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 40 | methods: { 41 | 42 | clickOpenExampleModalButton: async function() { 43 | this.goto('/welcome/hello'); 44 | // Or, without deep links, instead do: 45 | // ``` 46 | // this.modal = 'example'; 47 | // ``` 48 | }, 49 | 50 | closeExampleModal: async function() { 51 | this.goto('/welcome'); 52 | // Or, without deep links, instead do: 53 | // ``` 54 | // this.modal = ''; 55 | // ``` 56 | }, 57 | 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/components/stripe-card-element.component.less: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | [parasails-component='stripe-card-element'] { 5 | .card-element-wrapper { 6 | position: relative; 7 | .card-element { 8 | padding-top: 0; 9 | padding-bottom: 0; 10 | padding-right: 30px; 11 | &.pseudofocused { 12 | // These should mimic your normal form inputs' :focus styles: 13 | border-color: #80bdff; 14 | outline: 0; 15 | box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); 16 | } 17 | } 18 | .status-indicator { 19 | font-size: 15px; 20 | position: absolute; 21 | right: 14px; 22 | top: 8px; 23 | &.hidden { 24 | display: none; 25 | } 26 | &.syncing { 27 | -webkit-animation: fa-spinner-rotate 1.5s infinite linear; 28 | animation: fa-spinner-rotate 1.5s infinite linear; 29 | @-webkit-keyframes fa-spinner-rotate { 30 | 0% { 31 | -webkit-transform: rotate(0deg); 32 | } 33 | 100% { 34 | -webkit-transform: rotate(360deg); 35 | } 36 | } 37 | @keyframes rotate-clockwise { 38 | 0% { 39 | -ms-transform: rotate(0deg); 40 | transform: rotate(0deg); 41 | } 42 | 100% { 43 | -ms-transform: rotate(360deg); 44 | transform: rotate(360deg); 45 | } 46 | } 47 | } 48 | } 49 | &.secret-card-element-wrapper { 50 | opacity: 0; 51 | height: 1px; 52 | } 53 | } 54 | 55 | @media screen and (max-width: 450px) { 56 | .card-element-wrapper { 57 | .card-element { 58 | padding-right: 20px; 59 | } 60 | .status-indicator { 61 | right: 9px; 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/register/default.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/register/default.js` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * This is the default Grunt tasklist that will be executed if you 7 | * run `grunt` in the top level directory of your app. It is also 8 | * called automatically when you start Sails in development mode using 9 | * `sails lift` or `node app` in a development environment. 10 | * 11 | * For more information see: 12 | * https://sailsjs.com/anatomy/tasks/register/default.js 13 | * 14 | */ 15 | module.exports = function (grunt) { 16 | 17 | <% 18 | // ┌─┐┌─┐┬┬ ┌─┐ ┬ ┬┌┐┌┬┌─┌─┐┬─┐ ╔═╗╔╗╔╔═╗╔╗ ╦ ╔═╗╔╦╗ 19 | // └─┐├─┤││ └─┐───│ ││││├┴┐├┤ ├┬┘ ║╣ ║║║╠═╣╠╩╗║ ║╣ ║║ 20 | // └─┘┴ ┴┴┴─┘└─┘ ┴─┘┴┘└┘┴ ┴└─┘┴└─ ╚═╝╝╚╝╩ ╩╚═╝╩═╝╚═╝═╩╝ooo 21 | // ┌─ ┌┬┐┌─┐┌─┐┌─┐┬ ┬┬ ┌┬┐ ─┐ 22 | // │─── ││├┤ ├┤ ├─┤│ ││ │ ───│ 23 | // └─ ─┴┘└─┘└ ┴ ┴└─┘┴─┘┴ ─┘ 24 | if (linker) { %> 25 | grunt.registerTask('default', [ 26 | // 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.) 27 | 'compileAssets', 28 | // 'babel', //« uncomment to ALSO transpile during development (for broader browser compat.) 29 | 'linkAssets', 30 | 'watch' 31 | ]); 32 | <% } 33 | // ┌─┐┌─┐┬┬ ┌─┐ ┬ ┬┌┐┌┬┌─┌─┐┬─┐ ╔╦╗╦╔═╗╔═╗╔╗ ╦ ╔═╗╔╦╗ 34 | // └─┐├─┤││ └─┐───│ ││││├┴┐├┤ ├┬┘ ║║║╚═╗╠═╣╠╩╗║ ║╣ ║║ 35 | // └─┘┴ ┴┴┴─┘└─┘ ┴─┘┴┘└┘┴ ┴└─┘┴└─ ═╩╝╩╚═╝╩ ╩╚═╝╩═╝╚═╝═╩╝ooo 36 | else { %> 37 | grunt.registerTask('default', [ 38 | // 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.) 39 | 'compileAssets', 40 | // 'babel', //« uncomment to ALSO transpile during development (for broader browser compat.) 41 | 'watch' 42 | ]); 43 | <% } %> 44 | 45 | }; 46 | -------------------------------------------------------------------------------- /lib/builtins/jsonfile/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | var _ = require('@sailshq/lodash'); 7 | var fsx = require('fs-extra'); 8 | var reportback = require('reportback')(); 9 | 10 | 11 | 12 | /** 13 | * Generate a JSON file 14 | * 15 | * @option {String} rootPath 16 | * @option {Dictionary} data 17 | * [@option {Boolean} force=false] 18 | * 19 | * @handlers success 20 | * @handlers error 21 | * @handlers alreadyExists 22 | */ 23 | module.exports = function jsonfile( options, handlers ) { 24 | 25 | // Provide default values for handlers 26 | handlers = reportback.extend(handlers, { 27 | alreadyExists: 'error' 28 | }); 29 | 30 | // Provide defaults and validate required options 31 | _.defaults(options, { 32 | force: false 33 | }); 34 | 35 | var missingOpts = _.difference([ 36 | 'rootPath', 37 | 'data' 38 | ], Object.keys(options)); 39 | if ( missingOpts.length ) { 40 | return handlers.invalid(new Error('Consistency violation: some required opts are missing: '+missingOpts)); 41 | } 42 | 43 | 44 | var rootPath = path.resolve( process.cwd() , options.rootPath ); 45 | 46 | // Only override an existing file if `options.force` is true 47 | fsx.exists(rootPath, function (exists) { 48 | if (exists && !options.force) { 49 | return handlers.alreadyExists('Something else already exists at ::'+rootPath); 50 | } 51 | 52 | if ( exists ) { 53 | fsx.remove(rootPath, function deletedOldINode (err) { 54 | if (err) { return handlers.error(err); } 55 | _afterwards_(); 56 | }); 57 | } 58 | else {_afterwards_();} 59 | 60 | function _afterwards_ () { 61 | fsx.outputJSON(rootPath, options.data, function (err){ 62 | if (err) { return handlers.error(err); } 63 | else {handlers.success();} 64 | }); 65 | } 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/pages/entrance/new-password.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Reset password

4 |
5 |
6 | 7 |
8 | 9 | 10 |
Please enter a password.
11 |
12 |
13 | 14 | 15 |
Your new password and confirmation do not match.
16 |
17 | 18 |
19 | Submit 20 |
21 |
22 |

Remember your password? Login

23 |
24 |
25 |
26 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 27 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/copy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/copy` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Copy files and/or folders. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/copy.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('copy', { 15 | dev: { 16 | files: [{ 17 | expand: true, 18 | cwd: './assets', 19 | src: ['**/*.!(coffee|less)'], 20 | dest: '.tmp/public' 21 | }] 22 | }, 23 | build: { 24 | files: [{ 25 | expand: true, 26 | cwd: '.tmp/public', 27 | src: ['**/*'], 28 | dest: 'www' 29 | }] 30 | }, 31 | beforeLinkBuildProd: { 32 | files: [{ 33 | expand: true, 34 | cwd: '.tmp/public/hash', 35 | src: ['**/*'], 36 | dest: '.tmp/public/dist' 37 | }] 38 | }, 39 | }); 40 | 41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42 | // This Grunt plugin is part of the default asset pipeline in Sails, 43 | // so it's already been automatically loaded for you at this point. 44 | // 45 | // Of course, you can always remove this Grunt plugin altogether by 46 | // deleting this file. But check this out: you can also use your 47 | // _own_ custom version of this Grunt plugin. 48 | // 49 | // Here's how: 50 | // 51 | // 1. Install it as a local dependency of your Sails app: 52 | // ``` 53 | // $ npm install grunt-contrib-copy --save-dev --save-exact 54 | // ``` 55 | // 56 | // 57 | // 2. Then uncomment the following code: 58 | // 59 | // ``` 60 | // // Load Grunt plugin from the node_modules/ folder. 61 | // grunt.loadNpmTasks('grunt-contrib-copy'); 62 | // ``` 63 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/pages/entrance/forgot-password.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Recover password

4 |
5 |

Enter your email address below to reset the password for your account.

6 |
7 | 8 |
9 | 10 |
Please enter a valid email address.
11 |
12 | 13 |
14 | Send reset link 15 |
16 |
17 |

Back to login

18 |
19 |
20 |

We've sent you a link to update your password.

21 |
22 |

If the email doesn’t arrive after a few minutes, try checking your spam folder. If you still can’t find it, please try again, or contact support.

23 |

Back to login

24 |
25 |
26 |
27 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 28 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/README.md.template: -------------------------------------------------------------------------------- 1 | # <%= appName %> 2 | 3 | a [Sails v1](https://sailsjs.com) application 4 | 5 | 6 | ### Links 7 | 8 | + [Sails framework documentation](https://sailsjs.com/get-started) 9 | + [Version notes / upgrading](https://sailsjs.com/documentation/upgrading) 10 | + [Deployment tips](https://sailsjs.com/documentation/concepts/deployment) 11 | + [Community support options](https://sailsjs.com/support) 12 | + [Professional / enterprise options](https://sailsjs.com/enterprise) 13 | 14 | 15 | ### Version info 16 | 17 | This app was originally generated on <%= String(new Date()) %> using Sails v<%= generatedWithSailsVersion %>. 18 | 19 | 20 | 21 | <% if (caviar) { %> 22 | This project's boilerplate is based on an expanded seed app provided by the [Sails core team](https://sailsjs.com/about) to make it easier for you to build on top of ready-made features like authentication, enrollment, email verification, and billing. For more information, [drop us a line](https://sailsjs.com/support). 23 | <% } %> 24 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/blueprints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Blueprint API Configuration 3 | * (sails.config.blueprints) 4 | * 5 | * For background on the blueprint API in Sails, check out: 6 | * https://sailsjs.com/docs/reference/blueprint-api 7 | * 8 | * For details and more available options, see: 9 | * https://sailsjs.com/config/blueprints 10 | */ 11 | 12 | module.exports.blueprints = { 13 | 14 | /*************************************************************************** 15 | * * 16 | * Automatically expose implicit routes for every action in your app? * 17 | * * 18 | ***************************************************************************/ 19 | 20 | // actions: false, 21 | 22 | 23 | /*************************************************************************** 24 | * * 25 | * Automatically expose RESTful routes for your models? * 26 | * * 27 | ***************************************************************************/ 28 | 29 | <% if (caviar) { %>rest: false,<% } else { %>// rest: true,<% } %> 30 | 31 | 32 | /*************************************************************************** 33 | * * 34 | * Automatically expose CRUD "shortcut" routes to GET requests? * 35 | * (These are enabled by default in development only.) * 36 | * * 37 | ***************************************************************************/ 38 | 39 | <% if (caviar) { %>shortcuts: false,<% } else { %>// shortcuts: true,<% } %> 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /lib/core-generators/action/templates/actions2.template: -------------------------------------------------------------------------------- 1 | <%if (verbose) { %>/** 2 | * <%= relPath %> 3 | * 4 | * @description :: Server-side action for handling incoming requests. 5 | * @help :: See https://sailsjs.com/docs/concepts/actions 6 | */ 7 | <% } %>module.exports = { 8 | 9 | 10 | friendlyName: <%= util.inspect(friendlyName) %>, 11 | 12 | 13 | description: <%= util.inspect(description) %>,<% if (!inferredViewTemplatePath) { %> 14 | 15 | 16 | inputs: { 17 | <% if (verbose) { %> 18 | 19 | // Expected request parameters go here. 20 | // 21 | // username: { 22 | // description: 'The username to access records for, e.g. "sgress454".' 23 | // type: 'string', 24 | // required: true 25 | // }, 26 | 27 | <% } %> 28 | },<% } %> 29 | 30 | 31 | exits: {<% 32 | if (inferredViewTemplatePath) { %> 33 | 34 | success: { 35 | viewTemplatePath: <%= util.inspect(inferredViewTemplatePath) %> 36 | }<% } 37 | 38 | if (verbose) { %> 39 | 40 | // Potential responses go here. 41 | // 42 | // e.g. 43 | // 44 | // success: { 45 | // viewTemplatePath: 'pages/profile.ejs' 46 | // }, 47 | // 48 | // notLoggedIn: { 49 | // responseType: 'redirect' 50 | // }, 51 | // 52 | // --and/or-- 53 | // 54 | // success: { 55 | // outputFriendlyName: 'A list of pets.', 56 | // outputType: [ {} ] 57 | // }, 58 | // 59 | // malformedUsername: { 60 | // responseType: 'badRequest' 61 | // }, 62 | // 63 | // coolYourJets: { 64 | // statusCode: 420 65 | // } 66 | 67 | <% } %> 68 | 69 | }, 70 | 71 | 72 | fn: <%= IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'async function' : 'function' %> (<% if (!inferredViewTemplatePath) { %>inputs<% } %>) { 73 | 74 | <% if (inferredViewTemplatePath) { %>// Respond with view. 75 | return {};<% } else { %>// All done. 76 | return;<% } %> 77 | 78 | } 79 | 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/pages/entrance/signup.page.js: -------------------------------------------------------------------------------- 1 | parasails.registerPage('signup', { 2 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 3 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 4 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 5 | data: { 6 | // Form data 7 | formData: { /* … */ }, 8 | 9 | // For tracking client-side validation errors in our form. 10 | // > Has property set to `true` for each invalid property in `formData`. 11 | formErrors: { /* … */ }, 12 | 13 | // Form rules 14 | formRules: { 15 | fullName: {required: true}, 16 | emailAddress: {required: true, isEmail: true}, 17 | password: {required: true}, 18 | confirmPassword: {required: true, sameAs: 'password'}, 19 | agreed: {required: true}, 20 | }, 21 | 22 | // Syncing / loading state 23 | syncing: false, 24 | 25 | // Server error state 26 | cloudError: '', 27 | 28 | // Success state when form has been submitted 29 | cloudSuccess: false, 30 | }, 31 | 32 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 33 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 34 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 35 | beforeMount: function() { 36 | //… 37 | }, 38 | mounted: async function() { 39 | //… 40 | }, 41 | 42 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 43 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 44 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 45 | methods: { 46 | 47 | submittedForm: async function() { 48 | if(this.isEmailVerificationRequired) { 49 | // If email confirmation is enabled, show the success message. 50 | this.cloudSuccess = true; 51 | } 52 | else { 53 | // Otherwise, redirect to the logged-in dashboard. 54 | // > (Note that we re-enable the syncing state here. This is on purpose-- 55 | // > to make sure the spinner stays there until the page navigation finishes.) 56 | this.syncing = true; 57 | window.location = '/'; 58 | } 59 | }, 60 | 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/uglify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/uglify` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Minify client-side JavaScript files using UglifyES. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/uglify.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('uglify', { 15 | dist: { 16 | src: ['.tmp/public/concat/production.js'], 17 | dest: '.tmp/public/min/production.min.js' 18 | }, 19 | options: { 20 | mangle: { 21 | reserved: [ 22 | 'AsyncFunction', 23 | 'SailsSocket', 24 | 'Promise', 25 | 'File', 26 | 'FileList', 27 | 'FormData', 28 | 'Location', 29 | 'RttcRefPlaceholder', 30 | ], 31 | keep_fnames: true//eslint-disable-line 32 | }, 33 | compress: { 34 | keep_fnames: true//eslint-disable-line 35 | } 36 | } 37 | }); 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | // This Grunt plugin is part of the default asset pipeline in Sails, 41 | // so it's already been automatically loaded for you at this point. 42 | // 43 | // Of course, you can always remove this Grunt plugin altogether by 44 | // deleting this file. But check this out: you can also use your 45 | // _own_ custom version of this Grunt plugin. 46 | // 47 | // Here's how: 48 | // 49 | // 1. Install it as a local dependency of your Sails app: 50 | // ``` 51 | // $ npm install grunt-contrib-uglify --save-dev --save-exact 52 | // ``` 53 | // 54 | // 55 | // 2. Then uncomment the following code: 56 | // 57 | // ``` 58 | // // Load Grunt plugin from the node_modules/ folder. 59 | // grunt.loadNpmTasks('grunt-contrib-uglify'); 60 | // ``` 61 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 62 | 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/send-password-recovery-email.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Send password recovery email', 5 | 6 | 7 | description: 'Send a password recovery notification to the user with the specified email address.', 8 | 9 | 10 | inputs: { 11 | 12 | emailAddress: { 13 | description: 'The email address of the alleged user who wants to recover their password.', 14 | example: 'rydahl@example.com', 15 | type: 'string', 16 | required: true 17 | } 18 | 19 | }, 20 | 21 | 22 | exits: { 23 | 24 | success: { 25 | description: 'The email address might have matched a user in the database. (If so, a recovery email was sent.)' 26 | }, 27 | 28 | }, 29 | 30 | 31 | fn: async function ({emailAddress}) { 32 | 33 | // Find the record for this user. 34 | // (Even if no such user exists, pretend it worked to discourage sniffing.) 35 | var userRecord = await User.findOne({ emailAddress }); 36 | if (!userRecord) { 37 | return; 38 | }//• 39 | 40 | // Come up with a pseudorandom, probabilistically-unique token for use 41 | // in our password recovery email. 42 | var token = await sails.helpers.strings.random('url-friendly'); 43 | 44 | // Store the token on the user record 45 | // (This allows us to look up the user when the link from the email is clicked.) 46 | await User.updateOne({ id: userRecord.id }) 47 | .set({ 48 | passwordResetToken: token, 49 | passwordResetTokenExpiresAt: Date.now() + sails.config.custom.passwordResetTokenTTL, 50 | }); 51 | 52 | // Send recovery email 53 | await sails.helpers.sendTemplateEmail.with({ 54 | to: emailAddress, 55 | subject: 'Password reset instructions', 56 | template: 'email-reset-password', 57 | templateData: { 58 | fullName: userRecord.fullName, 59 | token: token 60 | } 61 | }); 62 | 63 | } 64 | 65 | 66 | }; 67 | -------------------------------------------------------------------------------- /lib/core-generators/script/templates/script.template: -------------------------------------------------------------------------------- 1 | <% if (lang === 'js') { 2 | if (verbose) { %>/** 3 | * <%= relPath %> 4 | * 5 | * @description :: Shell script. 6 | * @help :: See https://sailsjs.com/docs/concepts/scripts 7 | */ 8 | <% } %>module.exports = { 9 | 10 | 11 | friendlyName: <%= util.inspect(friendlyName) %>, 12 | 13 | 14 | description: <%= util.inspect(description) %>,<% if (verbose) { %> 15 | 16 | 17 | inputs: { 18 | 19 | // Input definitions go here. 20 | // 21 | // username: { 22 | // description: 'The username to access records for.' 23 | // extendedDescription: 'For example, `--username=ryandahl`.', 24 | // example: 'ryandahl', 25 | // required: true 26 | // }, 27 | 28 | }, 29 | <% } 30 | if (inferredSuccessOutputFriendlyName) { %> 31 | 32 | 33 | exits: { 34 | 35 | success: { 36 | outputFriendlyName: <%= util.inspect(inferredSuccessOutputFriendlyName) %>, 37 | outputType: 'ref' 38 | }, 39 | 40 | },<% } %> 41 | 42 | 43 | fn: <%= IS_CURRENT_NODE_VERSION_CAPABLE_OF_AWAIT ? 'async function' : 'function' %> (<% if (verbose) { %>inputs<% } %>) {<% 44 | if (inferredSuccessOutputFriendlyName) { %> 45 | 46 | // Get <%= inferredSuccessOutputFriendlyName[0].toLowerCase() + inferredSuccessOutputFriendlyName.slice(1) %>. 47 | var <%= _.camelCase(inferredSuccessOutputFriendlyName) %>; 48 | // TODO 49 | sails.log('Getting <%= inferredSuccessOutputFriendlyName[0].toLowerCase() + inferredSuccessOutputFriendlyName.slice(1) %>...'); 50 | 51 | return <%= _.camelCase(inferredSuccessOutputFriendlyName) %>; 52 | 53 | <% } else { %> 54 | 55 | sails.log('Running custom shell script... (`sails run <%= scriptName %>`)'); 56 | 57 | <% } %>} 58 | 59 | 60 | }; 61 | <% } else if (lang === 'coffee') { 62 | throw new Error('Sorry, generating a shell script in CoffeeScript is not supported yet. Please feel free to send a pull request to http://npmjs.com/package/sails-generate'); 63 | } %> 64 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/htmlhintrc.template: -------------------------------------------------------------------------------- 1 | <%/* 2 | ////////////////////////////////////////////////////////////////////// 3 | // ┬ ┬┌┬┐┌┬┐┬ ╦ ╦╦╔╗╔╔╦╗┬─┐┌─┐ 4 | // ├─┤ │ ││││ ╠═╣║║║║ ║ ├┬┘│ 5 | // o┴ ┴ ┴ ┴ ┴┴─┘╩ ╩╩╝╚╝ ╩ ┴└─└─┘ 6 | // 7 | // > A few EJS-safe HTML linting rules for your Sails app. 8 | // 9 | // This file (`.htmlhintrc`) exists to help you catch common mistakes 10 | // in markup templates (e.g. views, emails) throughout your Sails app. 11 | // 12 | // For the sake of convention, the recommended settings are included 13 | // here out of the box. But, of course, feel free to change these 14 | // as you see fit. (Note that this file only helps if your text editor 15 | // supports htmlhint syntax highlighting. If you'd prefer not to use 16 | // this approach, don't hesitate to delete this file or swap it out.) 17 | // 18 | // [?] If you're unsure, swing by https://sailsjs.com/support 19 | // 20 | ////////////////////////////////////////////////////////////////////// 21 | */ %>{ 22 | "alt-require": true, 23 | "attr-lowercase": ["viewBox"], 24 | "attr-no-duplication": true, 25 | "attr-unsafe-chars": true, 26 | "attr-value-double-quotes": true, 27 | "attr-value-not-empty": false, 28 | "csslint": false, 29 | "doctype-first": false, 30 | "doctype-html5": true, 31 | "head-script-disabled": false, 32 | "href-abs-or-rel": false, 33 | "id-class-ad-disabled": true, 34 | "id-class-value": false,<% /* // << This has to be disabled or it freaks out about EJS syntax. */ %> 35 | "id-unique": true, 36 | "inline-script-disabled": true, 37 | "inline-style-disabled": false, 38 | "jshint": false, 39 | "space-tab-mixed-disabled": "space", 40 | "spec-char-escape": false,<% /* // << This has to be disabled or it freaks out about EJS syntax. */ %> 41 | "src-not-empty": true, 42 | "style-disabled": false, 43 | "tag-pair": true, 44 | "tag-self-close": false,<% /* // << This has to be disabled or it freaks out about EJS syntax. */ %> 45 | "tagname-lowercase": true, 46 | "title-require": false 47 | } 48 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/session.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Session Configuration 3 | * (sails.config.session) 4 | * 5 | * Use the settings below to configure session integration in your app. 6 | * (for additional recommended settings, see `config/env/production.js`) 7 | * 8 | * For all available options, see: 9 | * https://sailsjs.com/config/session 10 | */ 11 | 12 | module.exports.session = { 13 | 14 | /*************************************************************************** 15 | * * 16 | * Session secret is automatically generated when your new app is created * 17 | * Replace at your own risk in production-- you will invalidate the cookies * 18 | * of your users, forcing them to log in again. * 19 | * * 20 | ***************************************************************************/ 21 | secret: '<%= secret %>', 22 | 23 | 24 | /*************************************************************************** 25 | * * 26 | * Customize when built-in session support will be skipped. * 27 | * * 28 | * (Useful for performance tuning; particularly to avoid wasting cycles on * 29 | * session management when responding to simple requests for static assets, * 30 | * like images or stylesheets.) * 31 | * * 32 | * https://sailsjs.com/config/session * 33 | * * 34 | ***************************************************************************/ 35 | // isSessionDisabled: function (req){ 36 | // return !!req.path.match(req._sails.LOOKS_LIKE_ASSET_RX); 37 | // }, 38 | 39 | }; 40 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/views.js: -------------------------------------------------------------------------------- 1 | /** 2 | * View Engine Configuration 3 | * (sails.config.views) 4 | * 5 | * Server-sent views are a secure and effective way to get your app up 6 | * and running. Views are normally served from actions. Below, you can 7 | * configure your templating language/framework of choice and configure 8 | * Sails' layout support. 9 | * 10 | * For details on available options for configuring server-side views, check out: 11 | * https://sailsjs.com/config/views 12 | * 13 | * For more background information on views and partials in Sails, check out: 14 | * https://sailsjs.com/docs/concepts/views 15 | */ 16 | 17 | module.exports.views = { 18 | 19 | /*************************************************************************** 20 | * * 21 | * Extension to use for your views. When calling `res.view()` in an action, * 22 | * you can leave this extension off. For example, calling * 23 | * `res.view('homepage')` will (using default settings) look for a * 24 | * `views/homepage.ejs` file. * 25 | * * 26 | ***************************************************************************/ 27 | 28 | // extension: 'ejs', 29 | 30 | /*************************************************************************** 31 | * * 32 | * The path (relative to the views directory, and without extension) to * 33 | * the default layout file to use, or `false` to disable layouts entirely. * 34 | * * 35 | * Note that layouts only work with the built-in EJS view engine! * 36 | * * 37 | ***************************************************************************/ 38 | 39 | layout: 'layouts/layout' 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /lib/builtins/folder/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | var _ = require('@sailshq/lodash'); 7 | var fsx = require('fs-extra'); 8 | var reportback = require('reportback')(); 9 | 10 | 11 | 12 | 13 | /** 14 | * Generate a folder 15 | * 16 | * @option {String} rootPath 17 | * @option {Boolean} gitkeep 18 | * [@option {Boolean} force=false] 19 | * 20 | * @sb [success] 21 | * @sb alreadyExists 22 | * @sb invalid 23 | * @sb error 24 | */ 25 | module.exports = function ( options, sb ) { 26 | 27 | // Provide default values for sb 28 | sb = reportback.extend(sb, { 29 | alreadyExists: 'error', 30 | invalid: 'error' 31 | }); 32 | 33 | // Provide defaults and validate required options 34 | _.defaults(options, { 35 | force: false, 36 | gitkeep: false 37 | }); 38 | var missingOpts = _.difference([ 39 | 'rootPath' 40 | ], Object.keys(options)); 41 | if ( missingOpts.length ) {return sb.invalid(missingOpts);} 42 | 43 | 44 | var rootPath = path.resolve( process.cwd() , options.rootPath ); 45 | 46 | 47 | // Only override an existing folder if `options.force` is true 48 | fsx.lstat(rootPath, function(err) { 49 | var exists = !(err && err.code === 'ENOENT'); 50 | if (exists && err) {return sb.error(err);} 51 | 52 | if (exists && !options.force) { 53 | return sb.alreadyExists('Something else already exists at ::'+rootPath); 54 | } 55 | if (exists) { 56 | fsx.remove(rootPath, function deletedOldINode(err) { 57 | if (err) {return sb.error(err);} 58 | _afterwards_(); 59 | }); 60 | } else {_afterwards_();} 61 | 62 | function _afterwards_() { 63 | 64 | // Don't actually write the directory if this is a dry run. 65 | if (options.dry) {return sb.success();} 66 | 67 | // Create the directory 68 | fsx.mkdirs(rootPath, function directoryWasWritten(err) { 69 | if (err) {return sb.error(err);} 70 | // console.log('created dir at :::: ',rootPath); 71 | return sb.success(); 72 | }); 73 | } 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/app.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * app.js 3 | * 4 | * Use `app.js` to run your app without `sails lift`. 5 | * To start the server, run: `node app.js`. 6 | * 7 | * This is handy in situations where the sails CLI is not relevant or useful, 8 | * such as when you deploy to a server, or a PaaS like Heroku. 9 | * 10 | * For example: 11 | * => `node app.js` 12 | * => `npm start` 13 | * => `forever start app.js` 14 | * => `node debug app.js` 15 | * 16 | * The same command-line arguments and env vars are supported, e.g.: 17 | * `NODE_ENV=production node app.js --port=80 --verbose` 18 | * 19 | * For more information see: 20 | * https://sailsjs.com/anatomy/app.js 21 | */ 22 | 23 | 24 | // Ensure we're in the project directory, so cwd-relative paths work as expected 25 | // no matter where we actually lift from. 26 | // > Note: This is not required in order to lift, but it is a convenient default. 27 | process.chdir(__dirname); 28 | 29 | 30 | 31 | // Attempt to import `sails` dependency, as well as `rc` (for loading `.sailsrc` files). 32 | var sails; 33 | var rc; 34 | try { 35 | sails = require('sails'); 36 | rc = require('sails/accessible/rc'); 37 | } catch (err) { 38 | console.error('Encountered an error when attempting to require(\'sails\'):'); 39 | console.error(err.stack); 40 | console.error('--'); 41 | console.error('To run an app using `node app.js`, you need to have Sails installed'); 42 | console.error('locally (`./node_modules/sails`). To do that, just make sure you\'re'); 43 | console.error('in the same directory as your app and run `npm install`.'); 44 | console.error(); 45 | console.error('If Sails is installed globally (i.e. `npm install -g sails`) you can'); 46 | console.error('also run this app with `sails lift`. Running with `sails lift` will'); 47 | console.error('not run this file (`app.js`), but it will do exactly the same thing.'); 48 | console.error('(It even uses your app directory\'s local Sails install, if possible.)'); 49 | return; 50 | }//-• 51 | 52 | 53 | // Start server 54 | sails.lift(rc('sails')); 55 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/pages/entrance/login.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Sign in to your account

4 |
5 |
6 | 7 |
8 | 9 |
Please provide a valid email address.
10 |
11 |
12 | 13 |
Please enter your password.
14 |
15 |
16 | 17 | 18 |
19 | The credentials you entered are not associated with an account. Please check your email and/or password and try again. 20 | 21 |
22 | Sign in 23 |
24 |
25 |

Forgot your password?

26 |
27 |
28 |
29 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 30 | -------------------------------------------------------------------------------- /lib/core-generators/response/templates/response.template: -------------------------------------------------------------------------------- 1 | /** 2 | * <%= filename %> 3 | * 4 | * A custom response. 5 | * 6 | * Example usage: 7 | * ``` 8 | * return res.<%= id %>(); 9 | * // -or- 10 | * return res.<%= id %>(optionalData); 11 | * ``` 12 | * 13 | * Or with actions2: 14 | * ``` 15 | * exits: { 16 | * somethingHappened: { 17 | * responseType: '<%= id %>' 18 | * } 19 | * } 20 | * ``` 21 | * 22 | * ``` 23 | * throw 'somethingHappened'; 24 | * // -or- 25 | * throw { somethingHappened: optionalData } 26 | * ``` 27 | */ 28 | 29 | module.exports = function <%= id %>(optionalData) { 30 | 31 | // Get access to `req` and `res` 32 | var req = this.req; 33 | var res = this.res; 34 | 35 | // Define the status code to send in the response. 36 | var statusCodeToSet = 400; 37 | 38 | // If no data was provided, use res.sendStatus(). 39 | if (optionalData === undefined) { 40 | sails.log.info('Ran custom response: res.<%= id %>()'); 41 | return res.sendStatus(statusCodeToSet); 42 | } 43 | // Else if the provided data is an Error instance, if it has 44 | // a toJSON() function, then always run it and use it as the 45 | // response body to send. Otherwise, send down its `.stack`, 46 | // except in production use res.sendStatus(). 47 | else if (_.isError(optionalData)) { 48 | sails.log.info('Custom response `res.<%= id %>()` called with an Error:', optionalData); 49 | 50 | // If the error doesn't have a custom .toJSON(), use its `stack` instead-- 51 | // otherwise res.json() would turn it into an empty dictionary. 52 | // (If this is production, don't send a response body at all.) 53 | if (!_.isFunction(optionalData.toJSON)) { 54 | if (process.env.NODE_ENV === 'production') { 55 | return res.sendStatus(statusCodeToSet); 56 | } 57 | else { 58 | return res.status(statusCodeToSet).send(optionalData.stack); 59 | } 60 | } 61 | } 62 | // Set status code and send response data. 63 | else { 64 | return res.status(statusCodeToSet).send(optionalData); 65 | } 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/pages/account/edit-password.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Change password

5 |
6 | 7 |
8 |
9 |
10 | 11 | 12 |
Please enter a password or choose "Cancel".
13 |
14 |
15 |
16 |
17 | 18 | 19 |
Your new password and confirmation do not match.
20 |
21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 | Cancel 30 | Save changes 31 |
32 |
33 |
34 |
35 |
36 |
37 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 38 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/importer.less.template: -------------------------------------------------------------------------------- 1 | /** 2 | * importer.less 3 | * 4 | * By default, new Sails projects are configured to compile this file 5 | * from LESS to CSS. Unlike CSS files, LESS files are not compiled and 6 | * included automatically unless they are imported below. 7 | * 8 | * For more information see: 9 | * https://sailsjs.com/anatomy/assets/styles/importer-less 10 | */ 11 | 12 | <% if(!caviar) {%> 13 | // For example: 14 | // 15 | // @import 'variables/colors.less'; 16 | // @import 'mixins/foo.less'; 17 | // @import 'mixins/bar.less'; 18 | // @import 'mixins/baz.less'; 19 | // 20 | // @import 'mixins-and-variables.less'; 21 | // @import 'pages/login.less'; 22 | // @import 'pages/signup.less'; 23 | // 24 | // etc. 25 | 26 | /*! This special bang (!) comment is here to trick the `hash` Grunt task into working out-of-the-box, without any real CSS. You can delete this once you've imported ≥1 .less file as demonstrated above. */<% } 27 | else { %>// Mixins and variables (LESS mixins/variables only, no global selectors) 28 | @import 'mixins-and-variables/index.less'; 29 | 30 | // Overall layout (contains global selectors) 31 | @import 'bootstrap-overrides.less'; 32 | @import 'layout.less'; 33 | 34 | // Per-component styles 35 | @import 'components/stripe-card-element.component.less'; 36 | @import 'components/ajax-button.component.less'; 37 | @import 'components/modal.component.less'; 38 | @import 'components/cloud-error.component.less'; 39 | 40 | // Per-page styles 41 | @import 'pages/homepage.less'; 42 | @import 'pages/dashboard/welcome.less'; 43 | @import 'pages/entrance/signup.less'; 44 | @import 'pages/entrance/confirmed-email.less'; 45 | @import 'pages/entrance/login.less'; 46 | @import 'pages/entrance/forgot-password.less'; 47 | @import 'pages/entrance/new-password.less'; 48 | @import 'pages/account/account-overview.less'; 49 | @import 'pages/account/edit-password.less'; 50 | @import 'pages/account/edit-profile.less'; 51 | @import 'pages/legal/terms.less'; 52 | @import 'pages/legal/privacy.less'; 53 | @import 'pages/faq.less'; 54 | @import 'pages/contact.less'; 55 | @import 'pages/404.less'; 56 | @import 'pages/500.less'; 57 | @import 'pages/498.less';<% } %> 58 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/deliver-contact-form-message.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Deliver contact form message', 5 | 6 | 7 | description: 'Deliver a contact form message to the appropriate internal channel(s).', 8 | 9 | 10 | inputs: { 11 | 12 | emailAddress: { 13 | required: true, 14 | type: 'string', 15 | description: 'A return email address where we can respond.', 16 | example: 'hermione@hogwarts.edu' 17 | }, 18 | 19 | topic: { 20 | required: true, 21 | type: 'string', 22 | description: 'The topic from the contact form.', 23 | example: 'I want to buy stuff.' 24 | }, 25 | 26 | fullName: { 27 | required: true, 28 | type: 'string', 29 | description: 'The full name of the human sending this message.', 30 | example: 'Hermione Granger' 31 | }, 32 | 33 | message: { 34 | required: true, 35 | type: 'string', 36 | description: 'The custom message, in plain text.' 37 | } 38 | 39 | }, 40 | 41 | 42 | exits: { 43 | 44 | success: { 45 | description: 'The message was sent successfully.' 46 | } 47 | 48 | }, 49 | 50 | 51 | fn: async function({emailAddress, topic, fullName, message}) { 52 | 53 | if (!sails.config.custom.internalEmailAddress) { 54 | throw new Error( 55 | `Cannot deliver incoming message from contact form because there is no internal 56 | email address (\`sails.config.custom.internalEmailAddress\`) configured for this 57 | app. To enable contact form emails, you'll need to add this missing setting to 58 | your custom config -- usually in \`config/custom.js\`, \`config/staging.js\`, 59 | \`config/production.js\`, or via system environment variables.` 60 | ); 61 | } 62 | 63 | await sails.helpers.sendTemplateEmail.with({ 64 | to: sails.config.custom.internalEmailAddress, 65 | subject: 'New contact form message', 66 | template: 'internal/email-contact-form', 67 | layout: false, 68 | templateData: { 69 | contactName: fullName, 70 | contactEmail: emailAddress, 71 | topic, 72 | message, 73 | } 74 | }); 75 | 76 | } 77 | 78 | 79 | }; 80 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/lesshintrc.template: -------------------------------------------------------------------------------- 1 | { 2 | // ╦ ╔═╗╔═╗╔═╗╦ ╦╦╔╗╔╔╦╗┬─┐┌─┐ 3 | // ║ ║╣ ╚═╗╚═╗╠═╣║║║║ ║ ├┬┘│ 4 | // o╩═╝╚═╝╚═╝╚═╝╩ ╩╩╝╚╝ ╩ ┴└─└─┘ 5 | // Configuration designed for the lesshint linter. Describes a loose set of LESS 6 | // conventions that help avoid typos, unexpected failed builds, and hard-to-debug 7 | // selector and CSS rule issues. 8 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9 | // For more information about any of the rules below, check out the reference page 10 | // of all rules at https://github.com/lesshint/lesshint/blob/v6.3.6/lib/linters/README.md 11 | // If you're unsure or could use some advice, come by https://sailsjs.com/support. 12 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 13 | "singleLinePerSelector": false, 14 | "singleLinePerProperty": false, 15 | "zeroUnit": false, 16 | "idSelector": false, 17 | "propertyOrdering": false, 18 | "spaceAroundBang": false, 19 | "fileExtensions": [".less", ".css"], 20 | "excludedFiles": ["vendor.less"], 21 | "importPath": false, 22 | "borderZero": false, 23 | "hexLength": false, 24 | "hexNotation": false, 25 | "newlineAfterBlock": false, 26 | "spaceBeforeBrace": { 27 | "style": "one_space" 28 | }, 29 | "spaceAfterPropertyName": false, 30 | "spaceAfterPropertyColon": { 31 | "enabled": true, 32 | "style": "one_space" 33 | }, 34 | "maxCharPerLine": false, 35 | "emptyRule": false, 36 | "importantRule": true, 37 | "qualifyingElement": false 38 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 39 | // ^^ This last one is only disabled because the lesshint parser seems to have 40 | // a hard time distinguishing between things like `div.bar` and `&.bar`. 41 | // In this case, the ampersand has a distinct meaning, and it does not refer 42 | // to an element. (It's referring to the case where that class is matched at 43 | // the parent level, rather than talking about a descendant.) 44 | // https://github.com/lesshint/lesshint/blob/v6.3.6/lib/linters/README.md#qualifyingelement 45 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 46 | } 47 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/pages/account/edit-profile.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Update personal info

5 |
6 | 7 |
8 |
9 |
10 | 11 | 12 |
Please enter your full name.
13 |
14 |
15 |
16 |
17 | 18 | 19 |
Please enter a valid email address.
20 |
21 |
22 |
23 |
24 |
25 | There is already an account using that email address. 26 | 27 |
28 |
29 |
30 | Cancel 31 | Save changes 32 |
33 |
34 |
35 |
36 |
37 |
38 | <%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %> 39 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/hash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/hash` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Implement cache-busting for minified CSS and JavaScript files. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/hash.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('hash', { 15 | options: { 16 | mapping: '', 17 | srcBasePath: '', 18 | destBasePath: '', 19 | flatten: false, 20 | hashLength: 8, 21 | hashFunction: function(source, encoding){ 22 | if (!source || !encoding) { 23 | throw new Error('Consistency violation: Cannot compute unique hash for production .css/.js cache-busting suffix, because `source` and/or `encoding` are falsey-- but they should be truthy strings! Here they are, respectively:\nsource: '+require('util').inspect(source, {depth:null})+'\nencoding: '+require('util').inspect(encoding, {depth:null})); 24 | } 25 | return require('crypto').createHash('sha1').update(source, encoding).digest('hex'); 26 | } 27 | }, 28 | js: { 29 | src: '.tmp/public/min/*.js', 30 | dest: '.tmp/public/hash/' 31 | }, 32 | css: { 33 | src: '.tmp/public/min/*.css', 34 | dest: '.tmp/public/hash/' 35 | } 36 | }); 37 | 38 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 39 | // This Grunt plugin is part of the default asset pipeline in Sails, 40 | // so it's already been automatically loaded for you at this point. 41 | // 42 | // Of course, you can always remove this Grunt plugin altogether by 43 | // deleting this file. But check this out: you can also use your 44 | // _own_ custom version of this Grunt plugin. 45 | // 46 | // Here's how: 47 | // 48 | // 1. Install it as a local dependency of your Sails app: 49 | // ``` 50 | // $ npm install grunt-hash --save-dev --save-exact 51 | // ``` 52 | // 53 | // 54 | // 2. Then uncomment the following code: 55 | // 56 | // ``` 57 | // // Load Grunt plugin from the node_modules/ folder. 58 | // grunt.loadNpmTasks('grunt-hash'); 59 | // ``` 60 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /lib/builtins/copy/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | var _ = require('@sailshq/lodash'); 7 | var fsx = require('fs-extra'); 8 | var reportback = require('reportback')(); 9 | 10 | 11 | 12 | /** 13 | * Copy file from one place to another. 14 | * 15 | * @required {String} rootPath 16 | * The absolute path of the destination for the copied file. 17 | * 18 | * @required {String} templatePath 19 | * The relative path from the generator's `templates/` dir 20 | * to the source template whose contents will be copied. 21 | * 22 | * @required {String} templatesDirectory 23 | * An absolute path to the generator's `templates/` dir. 24 | * 25 | * @option {Boolean} force[=false] 26 | * 27 | * @sb.success 28 | * @sb.error 29 | * @sb.alreadyExists 30 | * @sb.invalid 31 | */ 32 | 33 | module.exports = function(options, sb) { 34 | sb = reportback.extend(sb, { 35 | alreadyExists: 'error', 36 | invalid: 'error' 37 | }); 38 | 39 | // Validate options and provide defaults. 40 | if (_.isUndefined(options.templatePath)) { 41 | return sb.invalid(new Error('Consistency violation: `templatePath` is required')); 42 | } 43 | if (_.isUndefined(options.templatesDirectory)) { 44 | return sb.invalid(new Error('Consistency violation: `templatesDirectory` is required')); 45 | } 46 | if (_.isUndefined(options.rootPath)) { 47 | return sb.invalid(new Error('Consistency violation: `rootPath` is required')); 48 | } 49 | _.defaults(options, { 50 | force: false 51 | }); 52 | 53 | // Compute the absolute path to copy from, given its relative path 54 | // from its source generator's `templates/` directory. 55 | // > `templatesDirectory` should be provided as an absolute path! 56 | var absSrcPath = path.resolve(options.templatesDirectory, options.templatePath); 57 | 58 | // Note that we don't read in the file as utf8 here, 59 | // since it's not necessarily text (e.g. could be a binary file) 60 | // 61 | // So instead, we use fsx.copy(), which takes care of this for us. 62 | fsx.copy(absSrcPath, options.rootPath, { 63 | clobber: !!options.force 64 | }, function(err) { 65 | if (err) { return sb(err); } 66 | 67 | return sb(); 68 | 69 | });// 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/i18n.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internationalization / Localization Settings 3 | * (sails.config.i18n) 4 | * 5 | * If your app will touch people from all over the world, i18n (or internationalization) 6 | * may be an important part of your international strategy. 7 | * 8 | * For a complete list of options for Sails' built-in i18n support, see: 9 | * https://sailsjs.com/config/i-18-n 10 | * 11 | * For more info on i18n in Sails in general, check out: 12 | * https://sailsjs.com/docs/concepts/internationalization 13 | */ 14 | 15 | module.exports.i18n = { 16 | 17 | /*************************************************************************** 18 | * * 19 | * Which locales are supported? * 20 | * * 21 | ***************************************************************************/ 22 | 23 | locales: ['en', 'es', 'fr', 'de'], 24 | 25 | /**************************************************************************** 26 | * * 27 | * What is the default locale for the site? Note that this setting will be * 28 | * overridden for any request that sends an "Accept-Language" header (i.e. * 29 | * most browsers), but it's still useful if you need to localize the * 30 | * response for requests made by non-browser clients (e.g. cURL). * 31 | * * 32 | ****************************************************************************/ 33 | 34 | // defaultLocale: 'en', 35 | 36 | /**************************************************************************** 37 | * * 38 | * Path (relative to app root) of directory to store locale (translation) * 39 | * files in. * 40 | * * 41 | ****************************************************************************/ 42 | 43 | // localesDirectory: 'config/locales' 44 | 45 | }; 46 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/views/layout-email.ejs: -------------------------------------------------------------------------------- 1 | <% /* Default layout for email templates */ %> 2 |
3 |
4 | Logo 5 |
6 |
7 | 8 | <%- body %> 9 | 10 |
11 |
12 | Help 13 |   |   14 | FAQ 15 |   |   16 | Terms of Service 17 |   |   18 | Privacy Policy 19 |
20 |

Please do not reply to this email. To get in touch with us, visit our help center. If you have not signed up for an account with us, please disregard this message.

21 |

(built with Sails.js)

22 |
23 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/api/controllers/entrance/update-password-and-login.js.template: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | friendlyName: 'Update password and login', 5 | 6 | 7 | description: 'Finish the password recovery flow by setting the new password and '+ 8 | 'logging in the requesting user, based on the authenticity of their token.', 9 | 10 | 11 | inputs: { 12 | 13 | password: { 14 | description: 'The new, unencrypted password.', 15 | example: 'abc123v2', 16 | required: true 17 | }, 18 | 19 | token: { 20 | description: 'The password token that was generated by the `sendPasswordRecoveryEmail` endpoint.', 21 | example: 'gwa8gs8hgw9h2g9hg29hgwh9asdgh9q34$$$$$asdgasdggds', 22 | required: true 23 | } 24 | 25 | }, 26 | 27 | 28 | exits: { 29 | 30 | success: { 31 | description: 'Password successfully updated, and requesting user agent is now logged in.' 32 | }, 33 | 34 | invalidToken: { 35 | description: 'The provided password token is invalid, expired, or has already been used.', 36 | responseType: 'expired' 37 | } 38 | 39 | }, 40 | 41 | 42 | fn: async function ({password, token}) { 43 | 44 | if(!token) { 45 | throw 'invalidToken'; 46 | } 47 | 48 | // Look up the user with this reset token. 49 | var userRecord = await User.findOne({ passwordResetToken: token }); 50 | 51 | // If no such user exists, or their token is expired, bail. 52 | if (!userRecord || userRecord.passwordResetTokenExpiresAt <= Date.now()) { 53 | throw 'invalidToken'; 54 | } 55 | 56 | // Hash the new password. 57 | var hashed = await sails.helpers.passwords.hashPassword(password); 58 | 59 | // Store the user's new password and clear their reset token so it can't be used again. 60 | await User.updateOne({ id: userRecord.id }) 61 | .set({ 62 | password: hashed, 63 | passwordResetToken: '', 64 | passwordResetTokenExpiresAt: 0 65 | }); 66 | 67 | // Log the user in. 68 | // (This will be persisted when the response is sent.) 69 | this.req.session.userId = userRecord.id; 70 | 71 | // In case there was an existing session, broadcast a message that we can 72 | // display in other open tabs. 73 | if (sails.hooks.sockets) { 74 | await sails.helpers.broadcastSessionChange(this.req); 75 | } 76 | 77 | } 78 | 79 | 80 | }; 81 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/js/components/ajax-button.component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * ----------------------------------------------------------------------------- 4 | * A button with a built-in loading spinner. 5 | * 6 | * @type {Component} 7 | * 8 | * @event click [emitted when clicked] 9 | * ----------------------------------------------------------------------------- 10 | */ 11 | 12 | parasails.registerComponent('ajaxButton', { 13 | // ╔═╗╦═╗╔═╗╔═╗╔═╗ 14 | // ╠═╝╠╦╝║ ║╠═╝╚═╗ 15 | // ╩ ╩╚═╚═╝╩ ╚═╝ 16 | props: [ 17 | 'syncing' 18 | ], 19 | 20 | // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ 21 | // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ 22 | // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ 23 | data: function (){ 24 | return { 25 | //… 26 | }; 27 | }, 28 | 29 | // ╦ ╦╔╦╗╔╦╗╦ 30 | // ╠═╣ ║ ║║║║ 31 | // ╩ ╩ ╩ ╩ ╩╩═╝ 32 | template: ` 33 | 44 | `, 45 | 46 | // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ 47 | // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ 48 | // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ 49 | beforeMount: function() { 50 | //… 51 | }, 52 | mounted: async function(){ 53 | //… 54 | }, 55 | beforeDestroy: function() { 56 | //… 57 | }, 58 | 59 | // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ 60 | // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ 61 | // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ 62 | methods: { 63 | 64 | click: async function(){ 65 | this.$emit('click'); 66 | }, 67 | 68 | } 69 | }); 70 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/tasks/config/watch.js.template: -------------------------------------------------------------------------------- 1 | /** 2 | * `tasks/config/watch` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Run predefined tasks whenever certain files are added, changed or deleted. 7 | * 8 | * For more information, see: 9 | * https://sailsjs.com/anatomy/tasks/config/watch.js 10 | * 11 | */ 12 | module.exports = function(grunt) { 13 | 14 | grunt.config.set('watch', { 15 | assets: { 16 | 17 | // Assets to watch: 18 | files: [ 19 | 'assets/**/*', 20 | 'tasks/pipeline.js', 21 | '!**/node_modules/**' 22 | ], 23 | 24 | // When assets are changed: 25 | tasks: [ 26 | <% 27 | // ┌─┐┌─┐┬┬ ┌─┐ ┬ ┬┌┐┌┬┌─┌─┐┬─┐ ╔═╗╔╗╔╔═╗╔╗ ╦ ╔═╗╔╦╗ 28 | // └─┐├─┤││ └─┐───│ ││││├┴┐├┤ ├┬┘ ║╣ ║║║╠═╣╠╩╗║ ║╣ ║║ 29 | // └─┘┴ ┴┴┴─┘└─┘ ┴─┘┴┘└┘┴ ┴└─┘┴└─ ╚═╝╝╚╝╩ ╩╚═╝╩═╝╚═╝═╩╝ooo 30 | // ┌─ ┌┬┐┌─┐┌─┐┌─┐┬ ┬┬ ┌┬┐ ─┐ 31 | // │─── ││├┤ ├┤ ├─┤│ ││ │ ───│ 32 | // └─ ─┴┘└─┘└ ┴ ┴└─┘┴─┘┴ ─┘ 33 | if (linker) { 34 | %>'syncAssets', 35 | 'linkAssets'<% 36 | } 37 | // ┌─┐┌─┐┬┬ ┌─┐ ┬ ┬┌┐┌┬┌─┌─┐┬─┐ ╔╦╗╦╔═╗╔═╗╔╗ ╦ ╔═╗╔╦╗ 38 | // └─┐├─┤││ └─┐───│ ││││├┴┐├┤ ├┬┘ ║║║╚═╗╠═╣╠╩╗║ ║╣ ║║ 39 | // └─┘┴ ┴┴┴─┘└─┘ ┴─┘┴┘└┘┴ ┴└─┘┴└─ ═╩╝╩╚═╝╩ ╩╚═╝╩═╝╚═╝═╩╝ooo 40 | else { 41 | %>'syncAssets'<% 42 | } 43 | %> 44 | ] 45 | } 46 | }); 47 | 48 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 49 | // This Grunt plugin is part of the default asset pipeline in Sails, 50 | // so it's already been automatically loaded for you at this point. 51 | // 52 | // Of course, you can always remove this Grunt plugin altogether by 53 | // deleting this file. But check this out: you can also use your 54 | // _own_ custom version of this Grunt plugin. 55 | // 56 | // Here's how: 57 | // 58 | // 1. Install it as a local dependency of your Sails app: 59 | // ``` 60 | // $ npm install grunt-contrib-watch --save-dev --save-exact 61 | // ``` 62 | // 63 | // 64 | // 2. Then uncomment the following code: 65 | // 66 | // ``` 67 | // // Load Grunt plugin from the node_modules/ folder. 68 | // grunt.loadNpmTasks('grunt-contrib-watch'); 69 | // ``` 70 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 71 | 72 | }; 73 | -------------------------------------------------------------------------------- /lib/builtins/file/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | var _ = require('@sailshq/lodash'); 7 | var fsx = require('fs-extra'); 8 | var reportback = require('reportback')(); 9 | 10 | 11 | 12 | 13 | /** 14 | * Generate a file using the specified string. 15 | * 16 | * @option {String} rootPath 17 | * @option {String} contents - the string contents to write to disk 18 | * [@option {Boolean} force=false] 19 | * [@option {Boolean} dry=false] 20 | * 21 | * @sb success 22 | * @sb error 23 | * @sb invalid 24 | * @sb alreadyExists 25 | */ 26 | 27 | module.exports = function(options, sb) { 28 | 29 | // provide default values for switchback 30 | sb = reportback.extend(sb, { 31 | alreadyExists: 'error' 32 | }); 33 | 34 | 35 | // Validate options and provide defaults. 36 | if (_.isUndefined(options.contents)) { 37 | return sb.invalid(new Error('Consistency violation: `contents` is required')); 38 | } 39 | if (_.isUndefined(options.rootPath)) { 40 | return sb.invalid(new Error('Consistency violation: `rootPath` is required')); 41 | } 42 | _.defaults(options, { 43 | force: false 44 | }); 45 | 46 | // In case we ended up here w/ a relative path, 47 | // resolve it using the process's CWD 48 | var rootPath = path.resolve(process.cwd(), options.rootPath); 49 | 50 | // Only override an existing file if `options.force` is true 51 | fsx.exists(rootPath, function(exists) { 52 | if (exists && !options.force) { 53 | return sb.alreadyExists('Something else already exists at ::' + rootPath); 54 | } 55 | 56 | // Don't actually write the file if this is a dry run. 57 | if (options.dry) { return sb.success(); } 58 | 59 | // Delete existing file if necessary 60 | (function _deleteExistingFileMaybe(proceed){ 61 | if (!exists) { return proceed(); } 62 | 63 | fsx.remove(rootPath, function (err) { 64 | if (err) { return proceed(err); } 65 | return proceed(); 66 | }); 67 | 68 | })(function (err) { 69 | if (err) { return sb(err); } 70 | 71 | // console.log('about to generate a file @ `'+rootPath+'`:',options.contents); 72 | 73 | fsx.outputFile(rootPath, options.contents, function (err){ 74 | if (err) { return sb(err); } 75 | 76 | return sb(); 77 | 78 | });// 79 | 80 | });// 81 | 82 | });// 83 | 84 | }; 85 | -------------------------------------------------------------------------------- /lib/core-generators/response/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | var _ = require('@sailshq/lodash'); 7 | 8 | 9 | // On initial require of this file: 10 | // 11 | // • Determine abs path to templates folder. 12 | var TEMPLATES_PATH = path.resolve(__dirname,'./templates'); 13 | 14 | 15 | /** 16 | * sails-generate-response 17 | * 18 | * Usage: 19 | * `sails generate response ` 20 | * 21 | * @type {Dictionary} 22 | */ 23 | module.exports = { 24 | 25 | 26 | templatesDirectory: TEMPLATES_PATH, 27 | 28 | 29 | targets: { 30 | 31 | './api/responses/:filename': { template: 'response.template' } 32 | 33 | }, 34 | 35 | 36 | /** 37 | * This `before()` function is run before generating targets. 38 | * It validates user input, configures defaults, gets extra 39 | * dependencies, etc. 40 | * 41 | * @param {Dictionary} scope 42 | * @param {Function} sb [callback] 43 | */ 44 | before: function(scope, sb) { 45 | 46 | // Make sure we're in the root of a Sails project. 47 | var pathToPackageJSON = path.resolve(scope.rootPath, 'package.json'); 48 | var invalidPackageJSON; 49 | try { 50 | require(pathToPackageJSON); 51 | } catch (unused) { 52 | invalidPackageJSON = true; 53 | } 54 | 55 | if (invalidPackageJSON) { 56 | return sb.invalid('Sorry, this command can only be used in the root directory of a Sails project.'); 57 | } 58 | if (!scope.rootPath) { 59 | return sb.invalid('Usage: sails generate response '); 60 | } 61 | 62 | // scope.args are the raw command line arguments. 63 | // 64 | // e.g. if you run: 65 | // sails generate response serverError bar baz 66 | // then: 67 | // scope.args = ['serverError', 'bar', 'baz'] 68 | // 69 | _.defaults(scope, { 70 | id: scope.args[0] 71 | }); 72 | 73 | 74 | if (!scope.id) { 75 | return sb.invalid('Usage: sails generate response '); 76 | } 77 | 78 | 79 | if (scope.coffee || scope.lang === 'coffee' || scope.ext === '.coffee') { 80 | console.warn('WARNING: CoffeeScript is not supported for this generator. Proceeding anyway...'); 81 | } 82 | 83 | 84 | // Derive appropriate filename 85 | if (_.isUndefined(scope.filename)) { 86 | scope.filename = scope.id + '.js'; 87 | } 88 | 89 | // Trigger callback with no error to proceed. 90 | return sb(); 91 | 92 | }// 93 | 94 | }; 95 | 96 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/assets/styles/components/modal.component.less: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * App-wide styles for our modals. 5 | */ 6 | 7 | [parasails-component='modal'] { 8 | -webkit-overflow-scrolling: touch;//« makes this actually scrollable on certain phones 9 | [purpose='modal-dialog'] { 10 | z-index: 100; 11 | position: relative; 12 | max-width: 700px; 13 | [purpose='modal-content'] { 14 | max-width: 700px; 15 | [purpose='modal-close-button'] { 16 | .btn-reset(); 17 | opacity: 0.6; 18 | &:hover { 19 | opacity: 1; 20 | } 21 | } 22 | } 23 | } 24 | // Custom styles for the Bootstrap modal: 25 | // (Want to use Bootstrap's default styles? Just comment out the rest of the rules below) 26 | .petticoat { 27 | position: fixed; 28 | width: 100%; 29 | height: 75px;// should cover topbar 30 | z-index: 50; 31 | left: 0px; 32 | top: 0px; 33 | background-color: @accent-white; 34 | } 35 | .modal-content { 36 | border-radius: 0px; 37 | border-color: @accent-white; 38 | padding-top: 50px; 39 | padding-bottom: 50px; 40 | padding-left: 25px; 41 | padding-right: 25px; 42 | .modal-header { 43 | border-bottom: none; 44 | display: block; 45 | position: relative; 46 | text-align: center; 47 | padding-bottom: 0px; 48 | padding-left: 0px; 49 | padding-right: 0px; 50 | padding-top: 0px; 51 | .modal-title { 52 | font-weight: @bold; 53 | } 54 | .modal-intro { 55 | margin-left: auto; 56 | margin-right: auto; 57 | color: @text-muted; 58 | margin-bottom: 20px; 59 | } 60 | hr { 61 | margin-top: 25px; 62 | margin-left: auto; 63 | margin-right: auto; 64 | margin-bottom: 10px; 65 | width: 100px; 66 | height: 2px; 67 | border-top: 2px solid @brand; 68 | } 69 | } 70 | .modal-body { 71 | padding-top: 10px; 72 | padding-bottom: 10px; 73 | padding-left: 0px; 74 | padding-right: 0px; 75 | } 76 | .modal-footer { 77 | padding-top: 25px; 78 | padding-bottom: 0px; 79 | padding-left: 0px; 80 | padding-right: 0px; 81 | margin-top: 10px; 82 | } 83 | } 84 | } 85 | 86 | // Modal backdrop styles are exposed globally here because this gets appended to the 87 | .modal-backdrop { 88 | background-color: @accent-white; 89 | &.show { 90 | opacity: 0.95; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/core-generators/generator/before.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var util = require('util'); 6 | var path = require('path'); 7 | var _ = require('@sailshq/lodash'); 8 | 9 | 10 | // Compatible Sails semver range. 11 | var SAILS_SVR = '^1.0.0'; 12 | 13 | 14 | /** 15 | * Before generating targets... 16 | * 17 | * @param {Dictionary} scope 18 | * @param {Function} done 19 | */ 20 | module.exports = function(scope, done) { 21 | 22 | // Look at arguments and adjust rootPath if necessary. 23 | if (scope.args) { 24 | 25 | if (scope.args[0]) { 26 | 27 | // Allow "type" to be specified as a CLI opt, for convenience. 28 | if (scope.type) { 29 | scope.generatorType = scope.type; 30 | } 31 | else { 32 | scope.generatorType = scope.args[0]; 33 | } 34 | 35 | // Adjust rootPath to put the new generator in a new folder. 36 | scope.rootPath = path.resolve(scope.rootPath, 'sails-generate-'+_.kebabCase(scope.generatorType)); 37 | 38 | } 39 | } 40 | 41 | 42 | if (!scope.generatorType) { 43 | return done(new Error( 44 | 'Missing argument: Please provide a `generatorType` for this generator.\n' + 45 | '(should refer to the core generator to override, e.g. `controller` -- or a new generator to implement, e.g. `augmented-reality-server`).' 46 | )); 47 | } 48 | 49 | 50 | _.defaults(scope, { 51 | github: _.defaults(scope.github || {}, { 52 | // i.e. 53 | // Would you mind telling me your username on GitHub? 54 | // (or favorite pseudonym) 55 | username: process.env.USER || '' 56 | }), 57 | year: (new Date()).getFullYear(), 58 | }); 59 | 60 | _.defaults(scope, { 61 | website: util.format('http://github.com/%s', scope.github.username), 62 | author: util.format('%s',scope.github.username) || 'a node.js/sails user', 63 | repository: _.defaults(scope.repository || {}, { 64 | type: 'git', 65 | url: util.format('git://github.com/%s/sails-generate-%s.git',scope.github.username, scope.generatorType) 66 | }), 67 | license: 'MIT', 68 | sailsSVR: SAILS_SVR 69 | }); 70 | 71 | 72 | // Determine an appropriate package name. 73 | // (Use a scoped package name if we have a github username) 74 | if (scope.github.username) { 75 | scope.packageName = ('@'+scope.github.username+'/' + 'sails-generate-'+scope.generatorType).toLowerCase(); 76 | } 77 | else { 78 | scope.packageName = ('sails-generate-'+scope.generatorType).toLowerCase(); 79 | } 80 | 81 | return done(); 82 | }; 83 | -------------------------------------------------------------------------------- /lib/core-generators/new/templates/config/http.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Server Settings 3 | * (sails.config.http) 4 | * 5 | * Configuration for the underlying HTTP server in Sails. 6 | * (for additional recommended settings, see `config/env/production.js`) 7 | * 8 | * For more information on configuration, check out: 9 | * https://sailsjs.com/config/http 10 | */ 11 | 12 | module.exports.http = { 13 | 14 | /**************************************************************************** 15 | * * 16 | * Sails/Express middleware to run for every HTTP request. * 17 | * (Only applies to HTTP requests -- not virtual WebSocket requests.) * 18 | * * 19 | * https://sailsjs.com/documentation/concepts/middleware * 20 | * * 21 | ****************************************************************************/ 22 | 23 | middleware: { 24 | 25 | /*************************************************************************** 26 | * * 27 | * The order in which middleware should be run for HTTP requests. * 28 | * (This Sails app's routes are handled by the "router" middleware below.) * 29 | * * 30 | ***************************************************************************/ 31 | 32 | // order: [ 33 | // 'cookieParser', 34 | // 'session', 35 | // 'bodyParser', 36 | // 'compress', 37 | // 'poweredBy', 38 | // 'router', 39 | // 'www', 40 | // 'favicon', 41 | // ], 42 | 43 | 44 | /*************************************************************************** 45 | * * 46 | * The body parser that will handle incoming multipart HTTP requests. * 47 | * * 48 | * https://sailsjs.com/config/http#?customizing-the-body-parser * 49 | * * 50 | ***************************************************************************/ 51 | 52 | // bodyParser: (function _configureBodyParser(){ 53 | // var skipper = require('skipper'); 54 | // var middlewareFn = skipper({ strict: true }); 55 | // return middlewareFn; 56 | // })(), 57 | 58 | }, 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /test/unit/generate.jsonfile.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var expect = require('./util/expect-handler'); 6 | var assert = require('./util/file-assertions'); 7 | var runBeforeAndAfter = require('./util/run-before-and-after'); 8 | 9 | var builtinGenerateJsonfile = require('../../lib/builtins/jsonfile'); 10 | 11 | 12 | 13 | describe('jsonfile generator', function() { 14 | 15 | // Set up generic before+after test lifecycle. 16 | runBeforeAndAfter(); 17 | 18 | before(function() { 19 | this.fn = builtinGenerateJsonfile; 20 | }); 21 | 22 | 23 | describe('with missing `data`', function() { 24 | 25 | before(function() { 26 | this.options = { 27 | rootPath: this.heap.alloc() 28 | }; 29 | }); 30 | 31 | it('should trigger `invalid`', expect('invalid')); 32 | }); 33 | 34 | 35 | describe('with missing `rootPath', function() { 36 | 37 | before(function() { 38 | this.options = { 39 | data: { 40 | foo: 'bar' 41 | } 42 | }; 43 | }); 44 | 45 | it('should trigger `invalid`', expect('invalid')); 46 | }); 47 | 48 | 49 | 50 | describe('with empty data', function() { 51 | 52 | before(function() { 53 | this.options = { 54 | rootPath: this.heap.alloc(), 55 | data: {} 56 | }; 57 | }); 58 | 59 | it('should trigger `success`', expect('success')); 60 | it('should create a file', assert.fileExists); 61 | 62 | }); 63 | 64 | 65 | 66 | describe('if file already exists', function() { 67 | 68 | before(function(cb) { 69 | this.options = { 70 | rootPath: this.heap.alloc(), 71 | data: { 72 | foo: 'bar' 73 | } 74 | }; 75 | 76 | // Create an extra file beforehand to simulate a collision 77 | this.heap.touch(this.options.rootPath, cb); 78 | }); 79 | 80 | it('should trigger "alreadyExists" handler', expect({ 81 | alreadyExists: true, 82 | success: 'Should not override existing file without `options.force`!' 83 | })); 84 | 85 | }); 86 | 87 | 88 | 89 | describe('if file already exists and `force` option is true', function() { 90 | 91 | before(function(cb) { 92 | this.options = { 93 | rootPath: this.heap.alloc(), 94 | data: { 95 | foo: 'bar' 96 | }, 97 | force: true 98 | }; 99 | 100 | // Create an extra file beforehand to simulate a collision 101 | this.heap.touch(this.options.rootPath, cb); 102 | }); 103 | 104 | it('should trigger `success`', expect('success')); 105 | 106 | }); 107 | 108 | 109 | }); 110 | -------------------------------------------------------------------------------- /lib/core-generators/sails.io.js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var path = require('path'); 6 | var _ = require('@sailshq/lodash'); 7 | var fsx = require('fs-extra'); 8 | var builtinCopy = require('../../builtins/copy'); 9 | 10 | 11 | /** 12 | * sails-generate-sails.io.js 13 | * 14 | * Usage: 15 | * `sails generate sails.io.js ` 16 | * 17 | * @type {Dictionary} 18 | */ 19 | 20 | module.exports = { 21 | 22 | before: function(scope, done) { 23 | 24 | // If this is being run as a child of the `new` generator, always put the file at assets/dependencies/sails.io.js. 25 | if (scope.generatorType === 'new') { 26 | scope.dest = path.resolve(scope.rootPath, 'assets', 'dependencies', 'sails.io.js'); 27 | } 28 | 29 | // Otherwise if an explicit destination is given, use that. 30 | else if (_.isArray(scope.args) && scope.args[0]) { 31 | scope.dest = path.resolve(scope.topLvlRootPath, scope.args[0]); 32 | } 33 | 34 | // Otherwise see if we can locate the current location of `sails.io.js`. 35 | else { 36 | 37 | scope.dest = (function() { 38 | 39 | // Try the Sails 1.0 path. 40 | var pathToTry = path.resolve(scope.topLvlRootPath, 'assets', 'dependencies', 'sails.io.js'); 41 | if (fsx.existsSync(pathToTry)) { 42 | return pathToTry; 43 | } 44 | // Try the pre-Sails-1.0 path. 45 | pathToTry = path.resolve(scope.topLvlRootPath, 'assets', 'js', 'dependencies', 'sails.io.js'); 46 | if (fsx.existsSync(pathToTry)) { 47 | return pathToTry; 48 | } 49 | throw new Error('Could not automatically determine location of `sails.io.js` in your project. Please specify it with `sails generate sails.io.js `.'); 50 | 51 | })(); 52 | 53 | } 54 | 55 | // This generator is intended to overwrite whatever sails.io.js they already have. 56 | scope.force = true; 57 | 58 | return done(); 59 | 60 | }, 61 | 62 | // ╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗ 63 | // ║ ╠═╣╠╦╝║ ╦║╣ ║ ╚═╗ 64 | // ╩ ╩ ╩╩╚═╚═╝╚═╝ ╩ ╚═╝ 65 | targets: { 66 | 67 | 'sails.io.js': { 68 | exec: function(scope, done) { 69 | // Set the templates directory to the `sails.io.js-dist` dependency folder. 70 | scope.templatesDirectory = path.dirname(require.resolve('sails.io.js-dist')); 71 | // Set the template path to the `sails.io.js` file in that folder. 72 | scope.templatePath = 'sails.io.js'; 73 | // Set the rootPath (the destination for the file) based on the `scope.dest` we determined above. 74 | scope.rootPath = scope.dest; 75 | // Copy the file. 76 | builtinCopy(scope, function(err) { 77 | if (err) { 78 | return done(err); 79 | } 80 | return done(); 81 | }); 82 | } 83 | } 84 | 85 | } 86 | }; 87 | --------------------------------------------------------------------------------