Repository: artificialio/sane
Branch: master
Commit: b4bfd1532b8d
Files: 86
Total size: 144.7 KB
Directory structure:
gitextract_bwpiq3rl/
├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .sane-cli
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── appveyor.yml
├── bin/
│ ├── sane
│ └── sane.cmd
├── ecosystem.json
├── package.json
├── src/
│ ├── cli.js
│ ├── commands/
│ │ ├── generate.js
│ │ ├── index.js
│ │ ├── install.js
│ │ ├── new.js
│ │ ├── sync.js
│ │ └── up.js
│ ├── helpers/
│ │ ├── dockerCompose.js
│ │ ├── ember.js
│ │ ├── npm.js
│ │ ├── promise.js
│ │ ├── sails.js
│ │ └── setUpTracking.js
│ ├── projectMeta.js
│ └── tasks/
│ ├── Project.js
│ ├── checkEnvironment.js
│ ├── copyToProject.js
│ ├── generate/
│ │ ├── createEmberConfig.js
│ │ └── runMachine.js
│ ├── getAppNames.js
│ ├── getTemplates.js
│ ├── modelConversion.js
│ ├── renameTemplate.js
│ └── trackCommand.js
├── templates/
│ ├── .gitignore
│ ├── client/
│ │ └── app/
│ │ ├── adapters/
│ │ │ └── application.js
│ │ └── serializers/
│ │ └── application.js
│ ├── docker-compose.yml.hbs
│ ├── fig.yml.hbs
│ ├── package.json.hbs
│ └── server/
│ ├── .sailsrc
│ ├── api/
│ │ ├── controllers/
│ │ │ └── AppController.js
│ │ └── responses/
│ │ ├── badRequest.js
│ │ ├── forbidden.js
│ │ ├── notFound.js
│ │ ├── ok.js
│ │ └── serverError.js
│ ├── assets/
│ │ └── index.html
│ └── config/
│ ├── autoreload.js.hbs
│ ├── blueprints.js
│ ├── connections.js.hbs
│ ├── models.js.hbs
│ └── routes.js
└── tests/
├── .eslintrc
├── acceptance/
│ ├── generateTest.js
│ ├── helpTest.js
│ ├── installTest.js
│ └── newTest.js
├── factories/
│ └── .gitkeep
├── fixtures/
│ ├── .gitkeep
│ ├── generate/
│ │ └── ember/
│ │ ├── acceptance-test-ember-user-expected.js
│ │ └── acceptance-test-empty-user-expected.js
│ └── new/
│ ├── acceptance-test-docker-expected.yml
│ └── acceptance-test-sane-cli-expected.js
├── helpers/
│ ├── .gitkeep
│ ├── acceptanceSetup.js
│ ├── assertFile.js
│ ├── assertFileEquals.js
│ ├── killCliProcess.js
│ ├── promise.js
│ ├── runCommand.js
│ └── sane.js
├── runner.js
└── unit/
├── analyticsTest.js
├── commands/
│ ├── generateTest.js
│ └── newTest.js
├── getAddonTest.js
├── helpers/
│ └── emberTest.js
└── tasks/
└── createEmberConfigTest.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": ["es2015", "stage-1"],
"plugins": ["transform-regenerator"]
}
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false
================================================
FILE: .eslintignore
================================================
tests/fixtures/**
templates/**
lib
# Note: node_modules ignored by default
================================================
FILE: .eslintrc
================================================
{
"parser": "babel-eslint",
"rules" : {
"strict" : 0,
"no-underscore-dangle" : 0,
"no-multi-spaces" : 0,
"no-shadow" : 0,
"consistent-return" : 2,
"no-use-before-define" : 2,
"new-parens" : 2,
"no-cond-assign" : 2,
"keyword-spacing" : 2,
"space-infix-ops" : 2,
"key-spacing" : [2, {"align": "colon"}],
"comma-dangle" : [2, "never"],
"no-multiple-empty-lines" : [2, {"max": 2}],
"quotes" : [2, "single"],
"eqeqeq" : [2, "smart"],
"wrap-iife" : [2, "inside"],
"indent" : [2, 2],
"brace-style" : [2, "1tbs"],
"spaced-comment" : [2, "always", {"exceptions":["-","+"]}],
"space-before-function-parentheses": [2, {
"anonymous" : "always",
"named" : "never"
}]
},
"env": {
"node": true,
"es6" : true
}
}
================================================
FILE: .gitignore
================================================
node_modules
bower_components
_site
_posts
#client stuff
# See client/.gitignore
# Server stuff
# See server/.gitignore
# Ignore configuration and automatically generated cruft
dbconfig.php
sitemap.xml
*.log
.sass-cache
# Operating system files
.Spotlight-V100
.Trashes
.DS_Store
.DS_Store?
ehthumbs.db
Thumbs.db
#Broccoli folder
tmp
#Editor files
*.sublime-*
*.idea
# ignore compiled files
lib
================================================
FILE: .npmignore
================================================
tests
src
================================================
FILE: .sane-cli
================================================
{}
================================================
FILE: .travis.yml
================================================
language: node_js
sudo: false
node_js:
- "0.12"
- "4"
before_install:
- npm config set spin false
- npm install -g sails
# - npm install -g codeclimate-test-reporter
script: "npm test"
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/aa208e58da24be802fcb
on_success: change
on_failure: always
on_start: false
# addons:
# code_climate:
# repo_token: c44929e370fd9994b6ea628ef65b13c87bb0c479903ef2740557adeab4e1500b
# after_script:
# - cat lcov.info | codeclimate
================================================
FILE: CHANGELOG.md
================================================
# SANE Stack Changelog
### Master
* [BUGFIX] No longer requires global ember-cli
### 0.1.0-beta.1
* [FEATURE] Added an `sane install` command that allows to install addons such as `sane install sane-auth`. More details on how to write addons comming soon! In the mean-time check out https://github.com/sane/sane-auth/
* [BREAKING BUGFIX] Only supports ember-cli 0.2.3 and above for the addon system
* [ENHANCEMENT] Switched to `enpeem` module
* [ENHANCEMENT] Switch to babeljs for better debugging statements.
* [BUGFIX] Fixed installTest
* [BUGFIX] Added logic to ensure case of resources generated through `sane generate` https://github.com/artificialio/sane/pull/143
* [ENHANCEMENT] Added [eslint](http://eslint.org/) through [mocha-eslint](https://github.com/BadgeLabs/mocha-eslint)
* [BUGFIX] Fixed docker root issue for linux
* [ENHANCEMENT] Added stack trace to errors in `sane new`
* [ENHANCEMENT] Use master version of [sails-hook-dev](https://github.com/balderdashy/sails-hook-dev) since `1.0.0` runs into issues on Linux
* [BUGFIX] Enabled SANE to run without requiring a global ember-cli installation.
### 0.1.0-alpha.7
* [ENHANCEMENT] Switched from traceur to babeljs for better error messages
* [ENHANCEMENT] Added diffing support to file generator
* [BUGFIX] Fixed error with npmInstall
### 0.0.24
* [FEATURE] Added autoreload of sails server using [sails-hook-autoreload](https://github.com/sgress454/sails-hook-autoreload) hook
* [ENHANCEMENT] Added [sails-hook-dev](https://github.com/balderdashy/sails-hook-dev) and removed morgan request logging.
* [ENHANCEMENT] Bumped lodash to latest `3.x` version because of latest blueprints.
* [BUGFIX] Improved `sane new` command in certain edge cases
* [BUGFIX] Fixed a sails response bug that view is missing.
* [BUGFIX] Fixed errors in `sane new --skip-npm`.
* [BUGFIX] Fixed tests on Windows and AppVeyor.
* [BUGFIX] Full Windows support.
* [ENHANCEMENT] Changed to using `expect` instead of `assert` from chai module to match ember-cli [commit 2435679](https://github.com/ember-cli/ember-cli/commit/24356790ba1a6aead425c8bddfd96f6cb06ab1cb).
* [FEATURE] Added sane install command that installs any sane addons published on npm with a 'sane-addon' keyword. These addons can add custom generators that are automatically tun by the install command.
### 0.0.23
* [BUGFIX] Fixed ember-cli version check on `sane new`.
### 0.0.22
* [BUGFIX] Reverted a planned windows change which fixed `sane up` on Mac/Linus
### 0.0.21
* [BUGFIX] Fixed `cleanupSails()` procedure when creating a new project.
* [BUGFIX] Installing `sane-cli` now locally on new project creation.
### 0.0.20
* [BUGFIX] Forgot a change to `superspawn` to fix the windows issues.
### 0.0.19
* [BUGFIX] Changed to `superspawn` to fix issues on windows
### 0.0.18
* [FEATURE] Added support for local sane-cli (so you can have a fixed sane-cli per project)
* [ENHANCEMENT] Improved boot2docker running check
* [BUGFIX] explicit `sane up --docker=false` now working
* [ENHANCEMENT] No analytics for tests anymore by adding undocumented `--skip-analytics` flag
* [BUGFIX] Fixed installation for certain Unix platforms
* [BUGFIX] Fixed installation for Windows: Traceur was not loading modules properly.
* [BUGFIX] Fixed sails assets path for usage without grunt
* [ENHANCEMENT] Creating a new project now cleans up Sails to be API only (still including assets folder for easy deployment)
### 0.0.17
* [ENHANCEMENT] Update `leek` version which improves start-up time for commands that don't use tracking.
* [FEATURE] Added redis support.
* [FEATURE] Added `--skip-sails` and `--skip-ember flags to the `up` command, if you want to only start the backend or frontend server.
### 0.0.16
* [BUGFIX] Fixed bug with `sane new` not working without `--docker`
* [BUGFIX] Possibly fixing the EHOSTUNREACH issue.
### 0.0.15
* [BUGFIX] Fixed some bug with `sails-disk`
* [BUGFIX] Fixed a bug with `sane up`, using local install as well as recognizing a changed client folder
* [BUGFIX] Showing the right `sails version` when running `sane new` with docker
### 0.0.14
* [FEATURE] Added [https://github.com/expressjs/morgan](morgan) hook to sails app so each http request gets logged
* [FEATURE] Added `sane new --skip-npm --skip-bower` flags to skip npm and bower installation.
* [FEATURE] You can now rename your client/server folders, by renaming them and then adding the new name to your .sane-cli `apps`. See Docs for details
* [FEATURE] Added `--verbose` flag for `sane up`, which displays additional `boot2docker` output, only on windows & mac
* [BREAKING ENHANCEMENT] On windows and mac your sails-container-server gets automatically forwarded to localhost. Right now only supports the fixed 1337 porty. Removed the `--port-forward/-p` flag.
* [ENHANCEMENT] `pod` option is now supported in your `.sane-cli`
* [BUGFIX] Changed tracking code to an GA App
* [BREAKING BUGFIX] For consistency the short version of the `--docker` flag is now `-D` for all commands
### 0.0.13
* [ENHANCEMENT] Updated `fs-extra` to 0.13.0
* [ENHANCEMENT] Switched again to `traceur` so no compilation step is needed.
* [FEATURE] Added new feature behind `sane up -p` flag, to forward docker IP to localhost (using boot2docker).
### 0.0.12
* [BUGFIX] Fixed `sane generate resource` command.
### 0.0.11
* [BUGFIX] Fixed & improved database setup and config
* [ENHANCEMENT] Switched to [traceur](https://github.com/google/traceur-compiler) for easier development and fewer dependencies
* [ENHANCEMENT] Disabled grunt hook for sails per default which should reduce CPU usage
* [ENHANCEMENT] Added package.json to root folder, preparing for having a local sane-cli in the future
* [ENHANCEMENT] Added .editorconfig for better development consistency
* [FEATURE] Added `--live-reload` option to `sane up` to proxy through to `ember-cli`
* [ENHANCEMENT] Added anonymous google analytics tracking for sane-cli usage that can be disabled in the .sane-cli via the `disableAnalytics` option
### 0.0.10
* [BUGFIX] fixed missing comma in connections.js
### 0.0.9
* [ENHANCEMENT] Updated [es6-shim](https://github.com/paulmillr/es6-shim) to 0.21.0
* [BUGFIX] Fixed setup of blueprints for ember-data, as well as the right database adapter. Would recommend to setup the project new, or run the following commands in the `server` folder: `npm i sails-generate-ember-blueprints --save`, `npm i lodash --save`, `npm i pluralize --save`, `sails generate ember-blueprints` and depending on your database: `npm i --save sails-mongo`
### 0.0.8
* [ENHANCEMENT] Made installation of Docker optional to get quick dev setup running.
* [FEATURE] Added basic `sane up` command. To start with docker add `--docker` flag
* [FEAURE] Added possibility to add a .sane-cli in your project root and/or home directory so you don't have to specifiy cli options such as --docker with each command
* [FEAURE] Added `sane generate resource name attribue:type` command, to add models on the frontend as well as the backend
* [ENHANCEMENT] Now showing proper docker ip on `sane up --docker`
* [ENHANCEMENT] Now showing better log output when setting up new project.
* [BUGFIX] Does not create git repo within client folder anymore
### 0.0.6
* [ENHANCEMENT] Updated [commander.js](https://github.com/tj/commander.js) to 2.5.0
* [FEATURE] added `--verbose` flag to `sane new` command to display more output for debugging purposes
* [BUGFIX] Fixed an issue that the setup would never complete if no db (or disk) was given
### 0.0.5
* [ENHANCEMENT] Added pleasent-progress for better output of setup progress [#11](https://github.com/artificialio/sane/issues/11)
### 0.0.4
* [ENHANCEMENT] Changed to new, smaller and tagged docker container.
* [FEATURE] You can now specify latest or stable node versions in the fig.yml, as well as a fixed 0.10.32
### 0.0.3
* [FEATURE] added a basic CLI that can set up a Sails Container with Fig and a separate ember project, linking them together.
================================================
FILE: CONTRIBUTING.md
================================================
# Getting Involved
Open source projects like SANE are possible because of people like you who are
willing to get involved. You can contribute to the success of SANE by reporting
bugs, suggesting features, learning how to use it and helping others, and/or
submitting code in pull requests.
This document is a set of guidelines for making your contrubtions as useful
and helpful as possible.
# Questions
If you are having difficulties using SANE or have a question about usage
please ask a question in the Gitter chat: https://gitter.im/artificialio/sane
# Issues
Think you've found a bug or have a new feature to suggest? Let us know!
## Reporting a Bug
1. Update to the most recent master release if possible. Your bug may have
already been fixed.
2. Search for similar issues. It's possible somebody has encountered this bug
already, in which case you can add a simple "+1" to indicate you're also
experiencing the issue. Or you can add more detail if you think it will
help the discussion.
3. Copy the full error logs and stack trace into the issue description.
4. Include relavant details about your environment like operating system,
SANE version, node/iojs version, Docker usage, database usage, etc.
5. Provide a demo that specifically shows the problem. This demo should be fully
operational with the exception of the bug you want to demonstrate. The more
pared down, the better. Issues with demos are prioritized. The best demos are
Github repositories or a Heroku app + Github repository, but often a simple
[gist](https://gist.github.com/) is enough to convey the issue.
6. Your issue will be verified. The provided demo will be tested for
correctness. The SANE team will work with you until your issue can be
verified.
7. Keep up to date with feedback from the SANE team on your ticket. Your
ticket may be closed if it becomes stale, with the assumption that you are
either no longer experiencing the issue or are no longer interested.
8. If possible, submit a Pull Request with a failing test. Better yet, take
a stab at fixing the bug yourself if you can!
The more information you provide, the easier it is to reproduce and validate
the bug and the faster a fix can start being worked on.
## Requesting a Feature
1. Search Issues for similar feature requests. It's possible somebody has
already asked for this feature or provided a pull request that is still
being discussed.
2. Provide a clear and detailed explanation of the feature you want and why it's
important to add. If you are adding a feature targeting a minority of users,
an addon for our upcoming addon eco-system might be the perfect fit.
3. If the feature is complex, consider writing some initial documentation for
it. If the feature is accepted it will need to be documented and
this will also help reviewers to understand it better. Submitting a
feature request with documentation will help shape the implemented API.
4. Attempt a Pull Request. If you are willing to help the project out, you can
submit a Pull Request. We always have more work to do than time to do it. If
you can write some code then that will speed the process along.
# Pull Requests
We love pull requests. Here's a quick guide:
1. Fork the repo.
2. Run the tests. Only pull requests with passing tests are accepted, and
it's great to know that you have a clean slate: `npm install && npm test`.
3. Add a test for your change. Only refactoring and documentation changes
require no new tests. If you are adding functionality or fixing a bug,
please add a test. If you need help writing tests, speak up in the
[Gitter chat](https://gitter.im/artificialio/sane).
4. Make the test pass by fixing the bug or adding the feature.
5. Commit your changes. If your pull request fixes an issue, specify it in the
commit message. Here's an example: `git commit - m "Close #52 Fix
generators"`
6. Push to your fork and submit a pull request. In the pull-request title,
please prefix it with one of our tags: BUGFIX, FEATURE, ENHANCEMENT or
INTERNAL
* FEATURE and ENHANCEMENT tags are for things that users are interested in.
Avoid super technical talk. Craft a concise description of the change.
- FEATURE tag is a standalone new addition, an example of this would be
adding a new command
- ENHANCEMENT tag is an improvement on an existing feature
* BUGFIX tag is a link to a bug + a link to a patch.
* INTERNAL tag is an internal log of changes.
In the description, please provide us with some explanation of why you made
the changes you made. For new features make sure to explain a standard use
case to us.
If a change requires a user to change their configuration or
`package.json` also add a BREAKING tag within the brackets
before any other tags (example [BREAKING BUGFIX]).
We try to be quick about responding to tickets but sometimes we get a bit
backlogged. If the response is slow, try to find someone on
[Gitter chat](https://gitter.im/artificialio/sane) to give the ticket a review.
Some things that will increase the chance that your pull request is accepted:
* Use Node idioms and helpers.
* Include tests that fail without your code, and pass with it.
* Update the documentation, the surrounding one, examples elsewhere, guides,
whatever is affected by your contribution.
#### Syntax
Try to follow the conventions you see used in the source already, this includes:
* Two space for indents, no tabs.
* No trailing whitespace. Blank lines should not have any space.
* Anonymous function declarations should have a space between the function
keyword and the parameters: `function (a, b) {...`
* Named functions should have no space between the function name and
the parameters: `function myFunc(a, b) {...`
* All commas should be followed by a space.
* Curly braces should be preceeded by a space.
* Cyclomatic functions like `if` should have a space before their opening
parenthesis: `if (true) {...`
#### Inline Documentation Guidelines
All inline documentation is being written using YUIDoc.
Here's an example of a method block:
```
/**
* My method description. Describe what it does. Like other pieces
* of your comment blocks, this can span multiple lines.
*
* @method methodName
* @param {String} foo Argument 1
* @param {Object} config A config object
* @param {String} config.name The name on the config object
* @param {Function} config.callback A callback function on the config object
* @param {Boolean} [extra=false] Do extra, optional work
* @return {Boolean} Returns true on success
*/
```
To learn more about YUIDoc, see: https://yui.github.io/yuidoc/
#### Code Words
* `thisPropertyName`
* `Global.Class.attribute`
* `thisFunction()`
* `Global.CONSTANT_NAME`
* `true`, `false`, `null`, `undefined` (when referring to programming values)
And in case we didn't emphasize it enough: **we love tests!**
NOTE: Partially copied from https://raw.githubusercontent.com/ember-cli/ember-cli/master/CONTRIBUTING.md
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) 2014 Markus Padourek, Artificial Labs and SANE Stack contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<img src="https://camo.githubusercontent.com/b8ecf54b15f51c7c992d6fce003b661c96d8acec/68747470733a2f2f63646e2e7261776769742e636f6d2f6172746966696369616c696f2f73616e652f67682d70616765732f5f696e636c756465732f73616e652d6c6f676f2e737667" width="400"/>
</p>
## SANE Stack [![Bountysource][bounty-badge]][bounty-badge-url] [![Gitter][gitter-badge]][gitter-badge-url]
[![Build Status][travis-badge]][travis-badge-url] [![Build status][appveyor-badge]][appveyor-badge-url] [![Dependency Status][david-badge]][david-badge-url] [![npm][npm-badge]][npm-badge-url]
> NOTE: This project, while exciting, is still an early prototype. While being mostly stable it is still being iterated with feature changes and improvements fairly regularly.
Sane - A Javascript fullstack and cli that uses two of the best frameworks, [Sails](http://sailsjs.org/) and [Ember](http://emberjs.com/), so you can rapidly create production-ready web applications. It takes away all the hassle setting up the full backend and frontend environment, embracing convention-over-configuration all the way, so you can focus just on shipping your app. Additionally this cli also supports Docker, using [Docker Compose](https://docs.docker.com/compose/), to automatically install dependencies such as your database and will make deployment easier.
Quickstart:
* `npm install -g sails sane-cli`
* `sane new project` creates a local project with [sails-disk](https://github.com/balderdashy/sails-disk). To install with [Docker](https://www.docker.com/) and production databases see [Options](http://sanestack.com/#sane-stack-options).
* `sane generate resource user name:string age:number` to generate a new API on the backend and models on the frontend
* `sane up` to start the sails server on `localhost:1337` as well as the ember dev server on `localhost:4200`.
* To work on your frontend-app you work as you would normally do with ember-cli on `localhost:4200`.
* You are now good to go.
> Note: If you use Docker, make sure you have [Docker Compose](http://docs.docker.com/compose/) installed.
>On Mac or Windows [Docker Toolbox](https://docs.docker.com/mac/step_one/) is required. This will install all of the necessary Docker tooling, including VirtualBox.
> For Linux see: [https://docs.docker.com/installation/ubuntulinux/](https://docs.docker.com/installation/ubuntulinux/)
## Documentation
The full documentation is available at: http://sanestack.com/
## Development
sane-cli is developed with ES6/7 functionality, using [traceur](https://github.com/google/traceur-compiler) to provide a cleaner code as well as a nice experience for us the developers.
To get started:
* `git clone https://github.com/artificialio/sane.git`
* `cd sane && npm install` to install the dependencies
* `npm link` to make sure you can test the master version globally
* If you add a new feature an according test would be appreciated
* `npm test` to run the test-suite
## Thanks
Thanks to [mphasize](https://github.com/mphasize) for creating [sails-generate-ember-blueprints](https://github.com/mphasize/sails-generate-ember-blueprints) which overwrites the default SailsJS JSON response to the one that Ember Data's `RESTAdapter` and `RESTSerializer` expects.
Thanks to [sails](https://github.com/balderdashy/sails) for that great backend framework
Thanks to [ember-cli](https://github.com/stefanpenner/ember-cli) contributors for all the great effort that has gone into this product and from which I have taken a lot of inspiration and code, especially in regards to testing.
## License
SANE Stack is [MIT Licensed](https://github.com/artificialio/sails-ember-starter-kit/blob/master/LICENSE.md).
## Built by
[gitter-badge]: https://badges.gitter.im/Join+Chat.svg
[gitter-badge-url]: https://gitter.im/sane/sane?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[travis-badge]: https://travis-ci.org/sane/sane.svg?branch=master
[travis-badge-url]: https://travis-ci.org/sane/sane
[david-badge]: https://img.shields.io/david/sane/sane.svg?style=flat
[david-badge-url]: https://david-dm.org/sane/sane
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/oku88ae3kxddbw14/branch/master?svg=true
[appveyor-badge-url]: https://ci.appveyor.com/project/Globegitter/sane/branch/master
[npm-badge]: https://img.shields.io/npm/v/sane-cli.svg
[npm-badge-url]: https://www.npmjs.com/package/sane-cli
[bounty-badge]: https://www.bountysource.com/badge/team?team_id=58969&style=bounties_received
[bounty-badge-url]: https://www.bountysource.com/teams/sane-stack?utm_source=Sane%20Stack&utm_medium=shield&utm_campaign=raised
[code-climate-badge]: https://codeclimate.com/github/sane/sane/badges/gpa.svg
[code-climate-badge-url]: https://codeclimate.com/github/sane/sane
Built with love by [Artificial Labs](http://artificial.io/) and [contributors](https://github.com/artificialio/sane/graphs/contributors) <3
================================================
FILE: appveyor.yml
================================================
# http://www.appveyor.com/docs/appveyor-yml
# Leave line endings as-is
init:
- git config --global core.autocrlf input
# Test against these versions of Node.js.
environment:
matrix:
# appveyor doesn't know what node 0.12.0 is, don't test with node for now.
#- nodejs_version: "0.12.0"
# io.js 1.x
- nodejs_version: "1.5.0"
# immediately finish build if one of the jobs fails.
matrix:
fast_finish: true
# Do not build on tags (GitHub only)
skip_tags: true
# if "- no appveyor" appears, skip the commit
skip_commits:
message: /\- no appveyor/
branches:
except:
- gh-pages # blacklist
platform:
- x64
# build cache to preserve files/folders between builds
cache:
- node_modules -> **\package.json # preserve "node_modules" directory in the root of build folder but will reset it if package.json is modified
- C:\nc # custom npm cache location
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node 0.STABLE.latest
- ps: Install-Product node $env:nodejs_version $env:platform
# Install PhantomJS
#- cinst PhantomJS
#- set path=%path%;C:\tools\PhantomJS\
#- dir C:\tools\PhantomJS
# Typical npm stuff.
- if not exist C:\nc md C:\nc
- npm config set cache C:\nc
- npm install -g sails ember-cli
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- npm version
- npm test
# Don't actually build.
build: off
# Set build version format here instead of in the admin panel.
version: "{build}"
================================================
FILE: bin/sane
================================================
#!/usr/bin/env node
'use strict';
// Provide a title to the process in `ps`
process.title = 'sane';
var path = require('path');
var resolve = require('resolve');
require('babel-polyfill');
resolve('sane-cli', {
basedir: process.cwd()
}, function (error, localCliPath){
var cli;
if (error) {
// require global sane-cli
cli = require(path.join('..', 'lib', 'cli'));
} else {
cli = require(path.join(localCliPath, '..', '..', 'lib', 'cli'));
}
cli(process.argv);
});
================================================
FILE: bin/sane.cmd
================================================
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\sane" %*
================================================
FILE: ecosystem.json
================================================
{
"apps" : [{
"name" : "SANE App",
"script" : "app.js",
"env": {
"COMMON_VARIABLE": "true"
},
"env_production" : {
"NODE_ENV": "production"
}
}],
"deploy" : {
"production" : {
"user" : "user",
"host" : "0.0.0.0",
"ref" : "origin/master",
"repo" : "git@github.com:artificialio/sane.git",
"path" : "/home/user/apps/sane/production/",
"post-deploy" : "cd client && npm install && bower install && ember build --environment=production && cp -rf dist/* ../server/assets/ && cp -f dist/index.html ../server/views/index.ejs && rm -rf dist/ && rm -rf tmp/ && cd ../server && npm install && pm2 startOrRestart ../ecosystem.json --env production"
},
"development" : {
"user" : "user",
"host" : "0.0.0.0",
"ref" : "origin/master",
"repo" : "git@github.com:artificialio/sane.git",
"path" : "/home/user/apps/sane/development/",
"post-deploy" : "cd client && npm install && bower install && ember build --environment=development && cp -rf dist/* ../server/assets/ && cp -f dist/index.html ../server/views/index.ejs && rm -rf dist/ && rm -rf tmp/ && cd ../server && npm install && pm2 startOrRestart ../ecosystem.json --env development"
}
}
}
================================================
FILE: package.json
================================================
{
"name": "sane-cli",
"version": "0.1.0-beta.1",
"trackingCode": "UA-46963158-10",
"description": "A command-line tool for creating full stack sails-ember applications using docker",
"main": "bin/sane",
"scripts": {
"pretest": "npm run lint && npm run build",
"prepublish": "npm run build",
"build": "babel src --out-dir lib",
"build:watch": "babel src --out-dir lib --watch",
"lint": "eslint bin/sane src tests",
"test": "node tests/runner",
"test-all": "node tests/runner all",
"autotest": "mocha --watch --reporter spec tests/**/*Test.js"
},
"bin": {
"sane": "./bin/sane"
},
"repository": {
"type": "git",
"url": "https://github.com/artificialio/sane"
},
"keywords": [
"sane",
"sane-cli",
"ember",
"ember.js",
"sails",
"sails.js",
"full-stack",
"fullstack",
"javascript",
"cli",
"docker",
"fig"
],
"author": "Markus Padourek <markus.padourek@gmail.com> (http://artificial.io/)",
"license": "MIT",
"bugs": {
"url": "https://github.com/artificialio/sane/issues"
},
"homepage": "http://sanestack.com",
"dependencies": {
"babel": "^6.5.2",
"babel-cli": "^6.5.1",
"captains-log": "^0.11.11",
"chalk": "^1.0.0",
"child-process-promise": "^1.0.1",
"commander": "~2.9.0",
"configstore": "^1.4.0",
"diff": "^2.2.1",
"ember-cli": "~2.3.0",
"enpeem": "git://github.com/globegitter/enpeem.git#skip-dry-run-prefix-features",
"exit": "^0.1.2",
"findup": "^0.1.5",
"fs-extra": "~0.26.5",
"handlebars": "~4.0.5",
"leek": "0.0.21",
"lodash": "^3.6.0",
"node-uuid": "^1.4.2",
"npm-which": "^2.0.0",
"pleasant-progress": "^1.0.2",
"pluralize": "^1.1.2",
"readline-sync": "^1.4.1",
"resolve": "^1.0.0",
"rsvp": "^3.0.18",
"verboser": "^1.0.3",
"walk-sync": "~0.2.6",
"which": "^1.0.9",
"yam": "0.0.18"
},
"devDependencies": {
"babel-eslint": "^5.0.0",
"babel-plugin-transform-regenerator": "^6.5.2",
"babel-preset-es2015": "^6.5.0",
"babel-preset-stage-1": "^6.5.0",
"babel-register": "^6.5.2",
"chai": "^3.5.0",
"chai-as-promised": "^5.2.0",
"eslint": "^2.2.0",
"glob": "7.0.0",
"mocha": "^2.1.0",
"node-require-timings": "1.0.0",
"tmp-sync": "^1.0.1"
},
"preferGlobal": true
}
================================================
FILE: src/cli.js
================================================
'use strict';
var program = require('commander');
var Yam = require('yam');
var { version, name, trackingCode } = require('../package.json');
var chalk = require('chalk');
var commands = require('./commands');
var setUpTracking = require('./helpers/setUpTracking');
// TODO(markus): Wrap everything into a promise, so you can call cli(args).then() or await cli(arg)
module.exports = function cli(args) {
// load config/default parameters from .sane-cli file in current and home directory
var config = new Yam('sane-cli').getAll();
// support the undocumented flag --skip-analytics, really just for testing.
var i = args.indexOf('--skip-analytics');
var skipAnalytics = [];
if (i !== -1) {
skipAnalytics = args.splice(i, 1);
}
var leek;
if (skipAnalytics.length === 0) {
leek = setUpTracking(trackingCode, name, version, config);
}
program
.version(version)
.usage('<command>');
// TODO: help command - should work as an alias to --help but also show help for subcommands
// program. Right now you can do 'sane new --help'. It would also be good to be able to do
// 'sane help new'
// .command('help [command]')
// .alias('h')
// .description('Shows the help, optionally for a single command')
// .action(function (command){ commands.help(command) });
// new command
program
.command('new <name>')
.alias('n')
.description('Creates a new sane project. Choose database via -d disk(default)|mongo|mysql|postgres')
.option('-d, --database [type]', 'Options: disk (default), mongo, mysql, postgres, redis', config.database || 'disk')
.option('-D, --docker [boolean]', 'Setup Sails server and dependencies within docker containers.', config.docker || false)
.option('-v, --verbose [boolean]', 'Show more detailed command output.', config.verbose || false)
.option('--skip-npm [boolean]', 'Skips npm installation for ember-cli project creation', config.skipNpm || false)
.option('--skip-bower [boolean]', 'Skips bower installation for ember-cli project creation', config.skipBower || false)
.action(function (name, options) {
commands.new(name, options, leek)
.catch(function (error) {
console.log(chalk.red(error.message));
console.log(error.stack);
});
});
program
.command('up')
.option('-D, --docker [boolean]', 'Run Sails server and dependencies within docker containers.', config.docker || false)
.option('-l, --live-reload [boolean]', 'Controls the live-reload functionality for ember-cli', true)
.option('-v, --verbose [boolean]', 'Show more detailed command output.', config.verbose || false)
.option('-e, --skip-ember [boolean]', 'Do not lift the ember server.', config.skipEmber || false)
.option('-s, --skip-sails [boolean]', 'Do not lift the sails (and database) server.', config.skipSails || false)
// .option('-v, --verbose', 'Show more detailed command output.')
.alias('serve')
.description('Starts the Sails and Ember server with a unified log.')
.action(function (options) {
commands.up(options, leek);
});
program
.command('generate <blueprint> [name] [attributes...]')
.option('-D, --docker [boolean]', 'Run Sails server and dependencies within docker containers.', config.docker || false)
.option('--pod [boolean]', 'Places newly generated resource in an Ember pod', config.pod || false)
.option('-f, --force [boolean]', 'Forces the command to overwrite existing files.', config.force || false)
.option('--skip-npm [boolean]', 'Skips any npm installation that the generator would run', config.skipNpm || false)
.alias('g')
.description('Generates new code for client and server from blueprints')
.action(function (blueprint, name, attributes, options) {
commands.generate(blueprint, name, attributes, options, leek);
});
program
.command('install <addonName...>')
.option('-v, --verbose [boolean]', 'Run Sails server and dependencies within docker containers.', config.verbose || false)
.option('-D, --docker [boolean]', 'Run Sails server and dependencies within docker containers.', config.docker || false)
.option('-f, --force [boolean]', 'Forces the command to overwrite existing files.', config.force || false)
.option('--skip-npm [boolean]', 'Skips npm installation for any packages your addon is installing', config.skipNpm || false)
.alias('i')
.description('Installs a sane addon via npm and runs its default generator.')
.action(function (addonName, options) {
commands.install(addonName, options, leek)
.catch(function (error) {
console.log(chalk.red(error.message));
console.log(error.stack);
});
});
// TODO: sync command: Farily complex. Needs to check which models exist on the client-side, which on the server-side
// and then create all the one that don't exist on either side and the rest make sure they are the same.
// For all attributes that only exist on the sails side (e.g. email) a substitute has to be found
// Think if it makes sense to have some minimal settings (flags, or .sanerc file?) to ignore certain models/attrinutes
// .command('sync [modelName]')
// .alias('s')
// .description('Syncs the client and server models.')
// .action(function (modelName){ commands.sync(modelName); });
program.on('--help', function (){
console.log('version: ' + version);
});
program.parse(args);
if (!program.args.length || program.args[0] === 'help') {
program.help();
}
};
================================================
FILE: src/commands/generate.js
================================================
'use strict';
// with ES6 the same as: var spawn = require('child_process').spawn
var { spawn } = require('child_process');
var chalk = require('chalk');
var modelConversion = require('../tasks/modelConversion');
var trackCommand = require('../tasks/trackCommand');
var getAppNames = require('../tasks/getAppNames');
var ember = require('../helpers/ember');
var sails = require('../helpers/sails');
var Project = require('../tasks/Project');
var copyToProject = require('../tasks/copyToProject');
var getTemplates = require('../tasks/getTemplates');
var runMachine = require('../tasks/generate/runMachine');
var path = require('path');
var log = require('captains-log')();
var { singular } = require('pluralize');
var serverName = getAppNames.server();
var clientName = getAppNames.client();
var defaultBlueprints = ['api', 'resource'];
var project;
var addonBlueprints;
/**
* @overview A generator is a one-time task that is being executed by sane.
* These tasks usually are usually related to generating code, copying over files,
* installing modules/addons or running sails and ember commands.
* This function loads and runs all the existing generators in your project.
* @param {String} blueprint - The name of the blueprint itself (e.g. resource)
* @param {String} [name] - An extra, simple string parameter usually used for dynamic naming
* @param {String} [attributes] - An extra list of parameters used by the generator, such as model attributes
* @param {Object} [options] - Options passed over by commander including all set flags such --verbose
* @param {Object} [leek] - If set this command is tracked to understand cli usage better
*/
module.exports = async function generate(blueprint, name, attributes, options, leek) {
name = name || '';
attributes = attributes || '';
options = options || {};
project = await Project.closest();
log.verbose('Your project details: ', project);
addonBlueprints = project.getAddonBlueprints();
if (typeof leek !== 'undefined') {
trackCommand(`generate ${blueprint} ${name} ${attributes}`, {}, leek);
}
if (defaultBlueprints.indexOf(blueprint) > -1) {
if (name !== undefined) {
var prepEmberOptions = ['g', 'resource', name];
if (options.pod) {
prepEmberOptions.push('--pod');
}
var emberOptions = prepEmberOptions.concat(modelConversion.toEmber(attributes));
var sailsOptions = ['generate', 'model', singular(name)].concat(modelConversion.toSails(attributes));
spawn(ember, emberOptions, { cwd: clientName, stdio: 'inherit' });
spawn(sails, sailsOptions, { cwd: serverName, stdio: 'inherit' });
spawn(sails, ['generate', 'controller', singular(name)], { cwd: serverName, stdio: 'inherit' });
} else {
console.log(chalk.yellow('A resource/api name is required, e.g. sane g resource user'));
}
} else if (addonBlueprints.indexOf(blueprint) > -1) {
var blueprintPath = project.getBlueprintPath(blueprint);
try {
log.verbose('Running client machine...');
await runMachine.client(blueprintPath, project.root, clientName, options);
} catch (error) {
console.log(chalk.red('The client-side generator errored, please report that.')
+ chalk.yellow(' Finishing up your installation though.'));
}
try {
log.verbose('Running server machine...');
await runMachine.server(blueprintPath, serverName, options);
} catch (error) {
console.log(chalk.red('The server-side generator errored, please report that.')
+ chalk.yellow(' Finishing up your installation though.'));
}
log.verbose('Machines done, copy over templates now...');
// now copy over templates
var templates = getTemplates(path.join(blueprintPath, 'generate'));
copyToProject(templates, project.root, options.force);
} else {
console.log(chalk.yellow('Blueprint ' + blueprint + ' is not supported'));
}
};
================================================
FILE: src/commands/index.js
================================================
'use strict';
// Load `*.js` under current directory as properties
// i.e., `User.js` will become `exports['User']` or `exports.User`
require('fs').readdirSync(__dirname).forEach(function (file) {
if (file.match(/.+\.js/g) !== null && file !== 'index.js') {
var name = file.replace('.js', '');
exports[name] = require('./' + file);
}
});
================================================
FILE: src/commands/install.js
================================================
'use strict';
// var installNpmPackage = require('../tasks/installNpmPackage');
var npmInstall = require('enpeem').install;
// automatically detects --verbose flag
var generate = require('./generate');
var chalk = require('chalk');
module.exports = async function install(addonNames, options, leek) {
// verbose.log(addonNames);
// verbose.log(options);
// if (typeof leek !== 'undefined') {
// trackCommand(`install ${addonName}`, options, leek);
// }
try {
// Note currently only supports installing one addon at a time.
var npmOptions = {
dependencies: addonNames,
// dryRun: options.skipNpm,
saveDev : true
};
try {
await npmInstall(npmOptions);
} catch (error) {
console.log(chalk.red(error.message));
console.log(error.stack);
throw error;
}
for (let addonName of addonNames) {
var atPos = addonName.indexOf('@');
if (atPos > -1) {
// cut out the @ and use the semVer instead of the *
addonName = addonName.slice(0, atPos);
} else {
addonName = addonName;
}
console.log(`${addonName} succesfully installed. Running it's default generator now...`);
await generate(addonName, '', '', {
force : options.force || false,
docker : options.docker || false,
skipNpm: options.skipNpm || false
}, leek);
console.log(`${addonName} generator succesfully set up.`);
}
// await exec(`sane generate ${pkgName}`);
// console.log('Addon succesfully installed.');
} catch (error) {
console.log(chalk.red(error.message));
console.log(error.stack);
throw error;
}
};
================================================
FILE: src/commands/new.js
================================================
'use strict';
var chalk = require('chalk');
var checkEnvironment = require('../tasks/checkEnvironment');
var dockerCompose = require('../helpers/dockerCompose')();
var fs = require('fs-extra');
var getAppNames = require('../tasks/getAppNames');
var getTemplates = require('../tasks/getTemplates');
var hbs = require('handlebars');
var npm = require('../helpers/npm');
var path = require('path');
var PleasantProgress = require('pleasant-progress');
var projectMeta = require('../projectMeta');
var { question } = require('readline-sync');
var renameTemplate = require('../tasks/renameTemplate');
var trackCommand = require('../tasks/trackCommand');
var { spawn } = require('child-process-promise');
var log = require('captains-log')();
var sails = require('../helpers/sails');
var serverName = getAppNames.server();
var clientName = getAppNames.client();
var progress = new PleasantProgress();
function normalizeOption(option) {
if (option === 'mongodb') {
return 'mongo';
}
if (option === 'postgres') {
return 'postgresql';
}
return option;
}
function prepareTemplate(name, variables) {
var template = fs.readFileSync(path.join(projectMeta.sanePath(), 'templates', name), 'utf8');
template = hbs.compile(template);
return template(variables);
}
function prepareDockerTemplate(name, option, user) {
var db = null;
var dockerUser = null;
var port = 0;
var isMysql = false;
if (option !== 'disk') {
db = option;
}
if (user) {
dockerUser = user;
}
switch (option) {
case 'mysql':
port = 3306;
isMysql = true;
break;
case 'postgresql':
port = 5432;
db = 'postgres';
break;
case 'mongo':
port = 27017;
break;
case 'redis':
port = 6379;
db = 'redis';
break;
}
return prepareTemplate(name, {
database : db,
port : port,
isMysql : isMysql,
serverName: serverName,
user : dockerUser
});
}
/*
* Make sure the executed commands work with docker enabled/disabled
*/
function dockerExec(cmd, runsWithDocker, silent, dryRun) {
silent = silent || (silent = false);
dryRun = dryRun || (dryRun = false);
var options = { stdio: silent && 'ignore' || 'inherit' };
var cmdMain;
var cmdArgs;
var cmds = cmd.split(' ');
if (runsWithDocker) {
cmdMain = dockerCompose;
cmdArgs = ['run', serverName].concat(cmds);
} else {
cmdMain = (process.platform === 'win32' ? cmds[0] + '.cmd' : cmds[0]);
cmdArgs = cmds.slice(1);
// Note(markus): this assumes that dockerExec is only used for executing serverCommands
options.cwd = serverName;
}
if (dryRun) {
// simulate an npm i --save dry-run, since npm does not currently have that
if (cmds[0] === 'npm' && (cmds[1] === 'i' || cmds[1] === 'install')) {
var filename = path.join(serverName, 'package.json');
var packageInfo = fs.readFileSync(filename);
var content = JSON.parse(packageInfo);
for (var i = 2; i < cmds.length; i++) {
if (cmds[i] !== '--save') {
var atPos = cmds[i].indexOf('@');
if (atPos > -1) {
// cut out the @ and use the semVer instead of the *
content.dependencies[cmds[i].slice(0, atPos)] = cmds[i].slice(atPos + 1);
} else {
content.dependencies[cmds[i]] = '*';
}
}
}
// have to return a promise
return new Promise(
function (resolve) {
fs.writeFileSync(filename, JSON.stringify(content, null, 2));
resolve();
}
);
}
// return an empty promise
return new Promise(
function (resolve) {
resolve();
}
);
} else {
return spawn(cmdMain, cmdArgs, options);
}
}
function getMigrateType(database) {
var migrateType = 'safe';
switch (database) {
case 'mysql':
migrateType = 'alter';
break;
case 'postgresql':
migrateType = 'alter';
break;
case 'mongo':
migrateType = 'safe';
break;
case 'redis':
migrateType = 'safe';
break;
}
return migrateType;
}
function isWrongProjectName(name) {
if (name.includes('.') && name.length > 1){
return true;
}
return false;
}
function cleanUpSails(options, silent){
// clean up sails to be API only
fs.removeSync(path.join('server', 'views'));
fs.removeSync(path.join('server', 'tasks'));
fs.removeSync(path.join('server', 'Gruntfile.js'));
var sailsPackages = ['grunt', 'ejs', 'grunt-contrib-clean', 'grunt-contrib-concat',
'grunt-contrib-copy', 'grunt-contrib-cssmin', 'grunt-contrib-jst', 'grunt-contrib-less',
'grunt-contrib-uglify', 'grunt-contrib-watch', 'grunt-sails-linker', 'grunt-sync'];
// only runs simple unbuilds, so does not need to run in docker,
// since that only seems to cause errors if anything
return dockerExec(`npm rm ${sailsPackages.join(' ')} --save`, false, silent);
}
module.exports = async function newProject(name, options, leek) {
var ember = (process.platform === 'win32' ? 'ember.cmd' : 'ember');
var localEmber = path.join(__dirname, '..', '..', 'node_modules', '.bin', ember);
// take the local ember version from the package.json.
var localEmberVersion = require(path.join(__dirname, '..', '..', 'node_modules', 'ember-cli', 'package.json')).version;
if (!checkEnvironment.emberExists()) {
ember = localEmber;
} else {
// Get globally installed ember version
var emberVersion = await spawn(npm, ['ls', '--global', '--json', '--depth=0', 'ember-cli'], { capture: ['stdout', 'stderr']}).then(function (result) {
if (options.verbose) {
console.log(`[spawn] stderr: ${result.stderr.toString()}`);
console.log(`[spawn] stdout: ${result.stdout.toString()}`);
}
return JSON.parse(result.stdout).dependencies['ember-cli'].version;
}, function (err) {
console.error(err);
});
// compare the different ember versions
if ( emberVersion !== localEmberVersion) {
var answer = question(chalk.gray(
`Detected different versions for sane's locally installed ` +
`ember-cli (${localEmberVersion}) and your ` +
`global one (${emberVersion}).\n` +
`Want to use sane's local Ember? (y/n):`) + ' (y) ') || 'y';
if (answer === 'y' || answer === 'Y') {
ember = localEmber;
}
}
}
var installMsg;
// --docker is set
if (options.docker) {
installMsg = 'Setting up Sails project and downloading latest Docker Containers.';
if (!checkEnvironment.dockerExists()) {
throw new Error(`sane requires the latest docker-compose to be installed. ` +
`Check https://github.com/artificialio/sane/blob/master/README.md for more details.`);
}
if (!checkEnvironment.isDockerRunning()) {
throw new Error('Make sure Docker is running and properly configured.');
}
} else {
installMsg = 'Setting up Sails project locally.';
if (!checkEnvironment.sailsExists()) {
throw new Error('sane requires the latest sails to be installed. Run npm install -g sails.');
}
}
if (isWrongProjectName(name)) {
throw new Error(`Sane currently does not support a projectname of '${name}'.`);
}
options.database = normalizeOption(options.database);
var silent = true;
if (options.verbose) {
silent = false;
}
// All checks are done, log command to analytics, then start command
// when running tests leek will be undefined
if (typeof leek !== 'undefined') {
trackCommand(`new ${name}`, options, leek);
}
console.log(`Sane version: ${projectMeta.version()}\n`);
// If . is passed it creates a new project in the current folder
// with the name taken from the parent
if (name !== '.') {
// Create the new folder
try {
fs.mkdirSync(name);
// change directory into projectRoot
process.chdir(name);
} catch(err) {
if (err.code === 'EEXIST') {
err.message = 'Creating a new folder failed. Check if the folder \'' + name + '\' already exists.';
throw err;
} else {
throw err;
}
}
}
var projectName = path.basename(process.cwd());
// Linux systems need the non-root user to be specified in docker-compose.yml
var prepareOptions = null;
var resetDockerUser = false;
if (process.platform === 'linux') {
prepareOptions = [`${dockerCompose}.yml.hbs`, options.database, process.getuid()];
resetDockerUser = true;
} else {
prepareOptions = [`${dockerCompose}.yml.hbs`, options.database];
}
fs.writeFileSync(path.join(`${dockerCompose}.yml`), prepareDockerTemplate.apply(null, prepareOptions));
fs.writeFileSync(path.join('package.json'), prepareTemplate('package.json.hbs', { name: projectName, version: projectMeta.version() }));
var cliConfig = {};
for (var opt in options) {
// exclude properties that are not cli options
if (options.hasOwnProperty(opt) &&
!opt.startsWith('_') &&
['commands', 'options', 'parent'].indexOf(opt) === -1) {
cliConfig[opt] = options[opt];
}
cliConfig.apps = [clientName, serverName];
cliConfig.disableAnalytics = false;
}
// creating a default .sane-cli based on the parameters used in the new command
fs.writeFileSync(path.join('.sane-cli'), JSON.stringify(cliConfig, null, 2));
try {
fs.mkdirSync(serverName);
} catch(err) {
if (err.code === 'EEXIST') {
err.message = `Creating a new folder failed. Check if the folder '${name}' already exists.`;
throw err;
} else {
throw err;
}
}
// TODO(markus): If we use spawn with stdio inherit we can print the proper output for fog
// should also fix the ember-cli output
console.log(chalk.green(installMsg));
var sailsVersion;
if (!options.docker) {
sailsVersion = await spawn(sails, ['version'], { capture: ['stdout', 'stderr'] })
.then(function (result) {
if (options.verbose) {
console.log(`[spawn] stderr: ${result.stderr.toString()}`);
console.log(`[spawn] stdout: ${result.stdout.toString()}`);
}
return result.stdout.toString();
}, function (err) {
console.log(err);
});
} else {
sailsVersion = await spawn(dockerCompose, ['run', 'server', 'sails', 'version'], { capture: ['stdout', 'stderr'] })
.then(function (result) {
if (options.verbose) {
console.log(`[spawn] stderr: ${result.stderr.toString()}`);
console.log(`[spawn] stdout: ${result.stdout.toString()}`);
}
return result.stdout.toString();
}, function (err) {
console.log(err);
});
}
process.stdout.write(`sails version: ${sailsVersion}`);
await dockerExec('sails new .', options.docker);
if (!options.skipNpm) {
progress.start(chalk.green('Installing Sails packages for tooling via npm.'));
}
// npm install needs to be run as root, so if necessary remove the user from fig.yml
if (resetDockerUser) {
fs.writeFileSync(path.join(`${dockerCompose}.yml`), prepareDockerTemplate(`${dockerCompose}.yml.hbs`, options.database));
}
var sailsPackages = ['sails-generate-ember-blueprints', 'lodash', 'pluralize',
'sails-hook-autoreload@~0.11.4', `sails-${options.database}`];
await dockerExec(`npm i ${sailsPackages.join(' ')} --save`, options.docker, silent, options.skipNpm);
await dockerExec('sails generate ember-blueprints', options.docker, silent, options.skipNpm);
if (!options.skipNpm) {
progress.stop();
console.log(chalk.green('Installed Sails packages for tooling via npm.') + '\n');
} else {
console.log(chalk.yellow('Warning: skipping npm tooling so you have to manually run the sails generator:'));
console.log(chalk.yellow(`'sails-generate-ember-blueprints' (${chalk.underline('https://github.com/mphasize/sails-generate-ember-blueprints')})\n`));
}
// Creating new ember project
console.log(chalk.green('Setting up Ember project locally.'));
var emberArgs = ['new', clientName, '--skip-git'];
if (options.skipNpm) {
emberArgs.push('--skip-npm');
}
if (options.skipBower) {
emberArgs.push('--skip-bower');
}
process.stdout.write('ember-cli ');
await spawn(ember, emberArgs, { stdio: 'inherit' });
progress.start(chalk.green('Running tooling scripts for Sane.'));
// copy over template files
// TODO(markus): Try to refactor with copyToProject.js
var templates = getTemplates(projectMeta.sanePath());
var templatesRoot = templates.root;
templates = templates.templates;
for (var i = 0; i < templates.length; i++) {
var templateInPath = path.join(templatesRoot, templates[i]);
var templateOutPath = renameTemplate(templates[i]);
if (templates[i].indexOf('models.js.hbs') > -1) {
fs.writeFileSync(templateOutPath, prepareTemplate(templates[i],
{ database: options.database, migrateType: getMigrateType(options.database) }));
} else if (templates[i].indexOf('connections.js.hbs') > -1) {
var host = 'localhost';
if (options.docker) {
host = 'db';
}
fs.writeFileSync(templateOutPath, prepareTemplate(templates[i], { host: host }));
} else if (templates[i].indexOf('autoreload.js.hbs') > -1) {
var pollingWatch = 'false';
if (options.docker) {
pollingWatch = 'true';
}
fs.writeFileSync(templateOutPath, prepareTemplate(templates[i],
{ pollingWatch: pollingWatch }));
} else {
fs.outputFileSync(templateOutPath, fs.readFileSync(templateInPath));
}
}
try {
await cleanUpSails(options, silent);
} catch (err) {
console.log('Cleaning up Sails had some erros. Relax, your app is still working perfectly and shiny.');
log.verbose('Error Message to report:', err);
}
if (!options.skipNpm) {
// this is to install sane-cli locally
try {
await spawn(npm, ['install']);
} catch (err) {
console.log('Could not install local sane-cli. Manuall run \'npm install\' and all should be fine.');
log.verbose('Error Message to report:', err);
}
}
progress.stop();
console.log(chalk.green('Sane Project \'' + projectName + '\' successfully created.'));
};
================================================
FILE: src/commands/sync.js
================================================
'use strict';
module.exports = function generate(/* modelName */) {
console.log('Not implemented yet.');
};
================================================
FILE: src/commands/up.js
================================================
'use strict';
var { spawn } = require('child_process');
var chalk = require('chalk');
var exit = require('exit');
var checkEnvironment = require('../tasks/checkEnvironment');
var trackCommand = require('../tasks/trackCommand');
var getAppNames = require('../tasks/getAppNames');
var ember = require('../helpers/ember');
var sails = require('../helpers/sails');
var dockerCompose = require('../helpers/dockerCompose')();
var serverName = getAppNames.server();
var clientName = getAppNames.client();
// require('es6-shim');
/*
* @params
* data - Buffer Object to print
* prepend - string to prepend to each line of output
* used so user can identify which command printed that line
* color - which color to print (cyan, blue, green, magenta, yellow, red)
* outputMode - log or error
*
*/
function printLines(data, prepend, color, outputMode) {
color = color || 'green';
outputMode = outputMode || 'log';
data = data.toString();
data = data.replace(/\r\n/g, '\n');
data = data.replace(/\r/g, '\n');
// a workaround, since .split adds an empty array element if the string ends with a newline
if (data.endsWith('\n')) {
data = data.slice(0, -1);
}
var outLines = data.split(/\n/);
for (var line of outLines) {
console[outputMode](chalk[color](prepend + '| ') + line);
}
}
// Note(markus): When starting to use fig this might not work
// see http://stackoverflow.com/questions/14332721/node-js-spawn-child-process-and-get-terminal-output-instantaneously
function runAndOutput(cmd, parameters, execOptions, printOptions) {
var runningCmd = spawn(cmd, parameters, execOptions);
if (printOptions.verbose === undefined) {
printOptions.verbose = true;
}
if (!execOptions.stdio && printOptions.verbose) {
var prepend = `${printOptions.prepend}`;
if (printOptions.prepend.length === 6) {
prepend += ' ';
}
var color = printOptions.color;
// use event hooks to provide a callback to execute when data are available:
runningCmd.stdout.on('data', function (data) {
printLines(data, prepend, color);
});
runningCmd.stderr.on('data', function (data) {
printLines(data, prepend, color, 'error');
});
}
// Doesn't do anything
// runningCmd.on('exit', function (code) {
// console.log(cmd + ' process exited with code ' + code);
// });
}
module.exports = async function up(options, leek) {
var emberProxy = 'http://127.0.0.1';
var serverPrintOptions = { prepend: serverName, color: 'blue' };
var clientPrintOptions = { prepend: clientName, color: 'green' };
if (!options.skipSails) {
if (options.docker && options.docker !== 'false') {
// TODO(markus): When 'sane up' was used and user switches to 'sane up --docker' there seem to be some issues
// with node_modules (server starting slow or not starting at all), which can be fixed by clearing and reinstalling
// running this on every fig up seems a bit wasteful. Try to find an automatic way that only needs to run once.
// await spawnPromise('fig', ['run', 'server', 'rm', '-rf', 'node_modules'], { stdio: 'inherit', env: process.env });
// await spawnPromise('fig', ['run', 'server', 'npm', 'i'], { stdio: 'inherit', env: process.env });
if (checkEnvironment.dockerMachineExists()) {
emberProxy = 'http://' + checkEnvironment.getDockerIp();
} else if (checkEnvironment.boot2dockerExists()) {
runAndOutput('boot2docker', ['ssh', '-L', '1337:localhost:1337'], { env: process.env },
{ prepend: 'boot2docker', color: 'yellow', verbose: options.verbose });
}
if (checkEnvironment.isDockerRunning()) {
runAndOutput(dockerCompose, ['up'], { stdio: 'inherit', env: process.env }, serverPrintOptions);
} else {
console.error('Please start docker/boot2docker and run the command again.');
exit();
}
} else {
// Note(markus):The opposite of above's TODO seems to work fine. If 'sane up --docker' is run and then sane up
runAndOutput(sails, ['lift'], { cwd: serverName, env: process.env }, serverPrintOptions);
}
}
if (!options.skipEmber) {
runAndOutput(ember, ['serve', '--proxy', `${emberProxy}:1337`, '--live-reload', options.liveReload],
{ cwd: clientName, env: process.env }, clientPrintOptions);
}
if (typeof leek !== 'undefined') {
// Only track command when processes started without issues
trackCommand('up', options, leek);
}
};
================================================
FILE: src/helpers/dockerCompose.js
================================================
'use strict';
var which = require('which').sync;
module.exports = function () {
try {
which('docker-compose');
return 'docker-compose';
} catch (err) {
return 'fig';
}
};
================================================
FILE: src/helpers/ember.js
================================================
'use strict';
var ember;
var path = require('path');
var emberBin = (process.platform === 'win32' ? 'ember.cmd' : 'ember');
var localEmber = path.join(__dirname, '..', '..', 'node_modules', '.bin', emberBin);
var checkEnvironment = require('../tasks/checkEnvironment');
var which = require('npm-which')(process.cwd());
if (!checkEnvironment.emberExists()) {
ember = localEmber;
} else {
ember = which.sync('ember');
}
module.exports = ember;
================================================
FILE: src/helpers/npm.js
================================================
'use strict';
var npm = (process.platform === 'win32' ? 'npm.cmd' : 'npm');
module.exports = npm;
================================================
FILE: src/helpers/promise.js
================================================
/* eslint no-proto:0 */
'use strict';
var RSVP = require('rsvp');
var Promise = RSVP.Promise;
module.exports = PromiseExt;
// Utility functions on the native CTOR need some massaging
module.exports.hash = function () {
return this.resolve(RSVP.hash.apply(null, arguments));
};
module.exports.denodeify = function () {
var fn = RSVP.denodeify.apply(null, arguments);
var Constructor = this;
var newFn = function () {
return Constructor.resolve(fn.apply(null, arguments));
};
newFn.__proto__ = arguments[0];
return newFn;
};
module.exports.filter = function () {
return this.resolve(RSVP.filter.apply(null, arguments));
};
module.exports.map = function () {
return this.resolve(RSVP.map.apply(null, arguments));
};
function PromiseExt(resolver, label) {
this._superConstructor(resolver, label);
}
PromiseExt.prototype = Object.create(Promise.prototype);
PromiseExt.prototype.constructor = PromiseExt;
PromiseExt.prototype._superConstructor = Promise;
PromiseExt.__proto__ = Promise;
PromiseExt.prototype.returns = function (value) {
return this.then(function () {
return value;
});
};
PromiseExt.prototype.invoke = function (method) {
var args = Array.prototype.slice(arguments, 1);
return this.then(function (value) {
return value[method].apply(value, args);
}, undefined, 'invoke: ' + method + ' with: ' + args);
};
PromiseExt.prototype.map = function (mapFn) {
var Constructor = this.constructor;
return this.then(function (values) {
return Constructor.map(values, mapFn);
});
};
PromiseExt.prototype.filter = function (mapFn) {
var Constructor = this.constructor;
return this.then(function (values) {
return Constructor.filter(values, mapFn);
});
};
================================================
FILE: src/helpers/sails.js
================================================
'use strict';
var sails = (process.platform === 'win32' ? 'sails.cmd' : 'sails');
module.exports = sails;
================================================
FILE: src/helpers/setUpTracking.js
================================================
'use strict';
var Leek = require('leek');
module.exports = function setUpTracking(trackingCode, name, version, config) {
function clientId() {
var ConfigStore = require('configstore');
var configStore = new ConfigStore('sane-cli');
var id = configStore.get('client-id');
if (id) {
return id;
} else {
id = require('node-uuid').v4().toString();
configStore.set('client-id', id);
return id;
}
}
// setup analytics
var leekOptions = {
trackingCode: trackingCode,
globalName : name,
name : clientId(),
version : version,
silent : config.disableAnalytics || false
};
return new Leek(leekOptions);
};
================================================
FILE: src/projectMeta.js
================================================
'use strict';
var path = require('path');
var which = require('which').sync;
// TODO(markus): Needs some refactoring
// Try to implement these not as functions but as attributes of the object and set it at each sane call
// BUG: projectRoot changes when we do a cd() command
var self = {
binaryPath: function () {
return which('sane');
},
sanePath: function () {
return path.resolve(__dirname, '../');
},
version: function () {
return require(self.sanePath() + '/package.json').version;
} // ,
// projectRoot: function () {
// return process.cwd();
// },
// serverRoot: function () {
// return path.join(self.projectRoot(), 'server');
// },
// clientRoot: function () {
// return path.join(self.projectRoot(), 'client')
// }
};
module.exports = self;
================================================
FILE: src/tasks/Project.js
================================================
'use strict';
var verbose = require('verboser');
var assign = require('lodash/object/assign');
var pluck = require('lodash/collection/pluck');
var where = require('lodash/collection/where');
var Promise = require('../helpers/promise');
var findup = Promise.denodeify(require('findup'));
var path = require('path');
var fs = require('fs');
async function closestPackageJSON(pathName) {
var directory = await findup(pathName, 'package.json');
return {
directory: directory,
pkg : require(path.join(directory, 'package.json'))
};
}
/**
The Project 'class' is tied to your package.json. It is instiantiated
by giving Project.closest the path to your project.
@class Project
@constructor
@param {String} root Root directory for the project
@param {Object} pkg Contents of package.json
*/
function Project(root, pkg) {
verbose.log(`init root: ${root}`);
this.root = root;
this.pkg = pkg;
this.addonPackages = undefined;
}
Project.prototype.dependencies = function (pkg, excludeDevDeps) {
pkg = pkg || this.pkg || require('package.json') || {};
var devDependencies = pkg.devDependencies;
if (excludeDevDeps) {
devDependencies = {};
}
return assign({}, devDependencies, pkg.dependencies);
};
/**
Returns a new project based on the first package.json that is found
in `pathName`.
@private
@static
@method closest
@param {String} pathName Path to your project
@return {Promise} Promise which resolves to a {Project}
*/
Project.closest = async function(pathName) {
pathName = pathName || process.cwd();
this.root = pathName;
var result = await closestPackageJSON(pathName);
verbose.log(`closest ${pathName} -> ${result.directory}`);
if (result.pkg && result.pkg.name === 'sane-cli') {
return null;
}
return new Project(result.directory, result.pkg);
};
Project.prototype.discoverAddons = function (root, pkg, excludeDevDeps) {
root = root || this.root;
pkg = pkg || this.pkg;
var addonPackages = [];
Object.keys(this.dependencies(pkg, excludeDevDeps)).forEach(function (name) {
if (name !== 'sane-cli') {
var addonPath = path.join(root, 'node_modules', name);
var addon = this.getIfAddon(addonPath);
if (addon) {
addonPackages.push(addon);
}
}
}, this);
if (addonPackages) {
this.addonPackages = addonPackages;
return addonPackages;
}
return [];
};
Project.prototype.getIfAddon = function (addonPath) {
var pkgPath = path.join(addonPath, 'package.json');
verbose.log('attemping to add: %s', addonPath);
if (fs.existsSync(pkgPath)) {
var addonPkg = require(pkgPath);
var keywords = addonPkg.keywords || [];
verbose.log(` - module found: ${addonPkg.name}`);
addonPkg['sane-addon'] = addonPkg['sane-addon'] || {};
if (keywords.indexOf('sane-addon') > -1) {
verbose.log(' - is addon, adding...');
var addonInfo = {
name: addonPkg.name,
path: addonPath,
pkg : addonPkg
};
return addonInfo;
} else {
verbose.log(' - no sane-addon keyword, not including.');
}
} else {
verbose.log(' - no package.json (looked at ' + pkgPath + ').');
}
return null;
};
Project.prototype.getAddonBlueprints = function (addonPackages) {
addonPackages = addonPackages || this.addonPackages || this.discoverAddons();
// this.supportedBluePrints(pluck(addonPackages, 'name'));
return pluck(addonPackages, 'name');
};
Project.prototype.getBlueprintPath = function (addonName, addonPackages) {
addonPackages = addonPackages || this.addonPackages || this.discoverAddons();
// this.supportedBluePrints(pluck(addonPackages, 'name'));
return pluck(where(addonPackages, { name: addonName }), 'path')[0];
};
module.exports = Project;
// function getIfAddon(folderPath) {
// var pkgPath = path.join(addonPath, 'package.json');
// verbose.log('attemping to add: %s', addonPath);
//
// if (fs.existsSync(pkgPath)) {
// var addonPkg = require(pkgPath);
// var keywords = addonPkg.keywords || [];
// verbose.log(' - module found: %s', addonPkg.name);
//
// addonPkg['ember-addon'] = addonPkg['ember-addon'] || {};
//
// if (keywords.indexOf('ember-addon') > -1) {
// verbose.log(' - is addon, adding...');
// this.discoverAddons(addonPath, addonPkg, true);
// this.addonPackages[addonPkg.name] = {
// path: addonPath,
// pkg: addonPkg
// };
// } else {
// verbose.log(' - no ember-addon keyword, not including.');
// }
// }
// }
// Project.prototype.addonBlueprintLookupPaths = function() {
// var addonPaths = this.addons.map(function(addon) {
// if (addon.blueprintsPath) {
// return addon.blueprintsPath();
// }
// }, this);
//
// return addonPaths.filter(Boolean).reverse();
// };
//
//
//
// Project.prototype.dependencies = function(pkg, excludeDevDeps) {
// pkg = pkg || this.pkg || require('package.json') || {};
//
// var devDependencies = pkg['devDependencies'];
// if (excludeDevDeps) {
// devDependencies = {};
// }
//
// return assign({}, devDependencies, pkg['dependencies']);
// };
// function handleFindupError(pathName, reason) {
// // Would be nice if findup threw error subclasses
// if (reason && /not found/i.test(reason.message)) {
// throw new NotFoundError('No project found at or up from: `' + pathName + '`');
// } else {
// throw reason;
// }
// }
================================================
FILE: src/tasks/checkEnvironment.js
================================================
'use strict';
/*
* Check things like DOCKER_HOST is set, docker/boot2docker/fig (depending on OS), sails and/or ember installed
* Also used to return the right docker IP if used
*/
var which = require('which').sync;
var { execSync, spawnSync } = require('child_process');
var npm = require('../helpers/npm');
var self = {
/*
* OSX and windows need boot2docker installed. Other Unix systems do not.
* process.platform can return: 'darwin', 'freebsd', 'linux', 'sunos' or 'win32'
*/
dockerExists: function () {
try {
// this should exist by default
which('docker-compose');
} catch (err) {
try {
// legacy executable as fallback
which('fig');
} catch (err) {
return false;
}
}
try {
which('docker');
} catch (err) {
return false;
}
return true;
},
// only use this function for windows and mac
// boot2docker is deprecated by docker-machine, however continue to check for legacy installations
boot2dockerExists: function () {
try {
which('boot2docker');
return true;
} catch (err) {
return false;
}
},
dockerMachineExists: function () {
try {
which('docker-machine');
return true;
} catch (err) {
return false;
}
},
isDockerRunning: function () {
var running = false;
if (['win32', 'darwin'].indexOf(process.platform) > -1) {
var command;
if (self.dockerMachineExists()) {
if (process.env.DOCKER_MACHINE_NAME === undefined) {
console.error('DOCKER environment variables are not set. see: https://docs.docker.com/machine/reference/env/');
return running;
}
command = 'docker-machine status ' + process.env.DOCKER_MACHINE_NAME;
} else if (self.boot2dockerExists()) {
command = 'boot2docker status';
}
if (command) {
var stdout = execSync(command, { encoding: 'utf-8' });
running = stdout.trim().toLowerCase() === 'running';
}
} else {
try {
execSync('docker info', { encoding: 'utf-8' });
running = true;
} catch (err) {
running = false;
}
}
return running;
},
emberExists: function () {
var res = spawnSync(npm, ['ls', '--global', '--json', '--depth=0', 'ember-cli']);
var stdoutJSON = JSON.parse(res.stdout);
if (stdoutJSON.dependencies && stdoutJSON.dependencies['ember-cli']) {
return true;
}
return false;
},
sailsExists: function () {
try {
which('sails');
return true;
} catch (err) {
return false;
}
},
getDockerIp: function () {
if (self.dockerExists() && self.isDockerRunning()) {
var ip = process.env.DOCKER_HOST;
// expects DOCKER_HOST to be of format: 'tcp://0.0.0.0:0'
ip = ip.slice(ip.lastIndexOf('/') + 1, ip.lastIndexOf(':'));
// simple check for valid ip
if (ip.length > 6) {
return ip;
}
}
return '';
}
};
module.exports = self;
================================================
FILE: src/tasks/copyToProject.js
================================================
'use strict';
var Project = require('./Project');
var path = require('path');
var fs = require('fs-extra');
var jsdiff = require('diff');
var chalk = require('chalk');
var { question } = require('readline-sync');
var EOL = require('os').EOL;
function highlightDiff(line) {
if (line[0] === '+') {
return chalk.green(line);
} else if (line[0] === '-') {
return chalk.red(line);
} else if (line.match(/^@@/)) {
return chalk.cyan(line);
} else {
return line;
}
}
module.exports = function copyToProject(templates, projectRoot, force) {
projectRoot = projectRoot || Project.closest().root;
console.log('force', force);
for (var i = 0; i < templates.templates.length; i++) {
var task = 'overwrite';
var template = templates.templates[i];
var sourcePath = path.join(templates.root, template);
var destPath = path.join(projectRoot, template);
var templateContent;
var destContent;
try {
if (fs.existsSync(destPath) && (typeof force === 'undefined' || force === false)) {
templateContent = fs.readFileSync(sourcePath, { encoding: 'utf-8' });
destContent = fs.readFileSync(destPath, { encoding: 'utf-8' });
if (templateContent === destContent) {
task = 'identical';
} else {
var err = new Error('File already exists');
err.name = 'FileExists';
throw err;
}
} else {
task = 'overwrite';
}
} catch (err) {
var answer = '';
while (['y', 'n', 'd'].indexOf(answer) === -1) {
answer = question(chalk.gray(`File ${template} already exists. Want to overwrite? (y/n/d):`) + ' (y) ') || 'y';
if (answer === 'y') {
// copySync just seems to copy over the file regardless if it exists or not.
fs.copySync(sourcePath, destPath);
} else if (answer === 'n') {
task = 'ignore';
} else if (answer === 'd') {
var diff = jsdiff.createPatch(
destPath, destContent, templateContent
);
var lines = diff.split('\n');
for (var j = 0; j < lines.length; j++) {
process.stdout.write(
highlightDiff(lines[j] + EOL)
);
}
task = 'again';
} else {
task = 'again';
console.log('Please choose either y for Yes, n for No or d for showing the file diff.');
}
}
} finally {
// now decide what to do
switch (task) {
case 'overwrite':
console.log(`${chalk.yellow('Overwriting')} ${template}`);
fs.copySync(sourcePath, destPath);
break;
case 'identical':
console.log(`${chalk.yellow('Identical')} ${template}`);
break;
case 'ignore':
console.log(`${chalk.yellow('Ignoring')} ${template}`);
break;
case 'again':
i--;
break;
default:
break;
}
}
}
};
================================================
FILE: src/tasks/generate/createEmberConfig.js
================================================
'use strict';
var path = require('path');
var fs = require('fs');
/**
* @overview This function executes the machine that is responsible for running client tasks
*
* @param {String} projectRoot - path to the project root
* @param {Object} config - contains an ENV object
* @param {Object} config.ENV - contains other objects that will be added to the ember-cli config
* @return {String} environmentPath - the path to the environment.js (config file) to write to.
* @return {String} newEnvironment - the new environment/configuration to write to that file.
*/
module.exports = function createEmberConfig(projectRoot, config) {
var environmentPath = path.join(projectRoot, 'client', 'config', 'environment.js');
var environment = fs.readFileSync(environmentPath, { encoding: 'utf8' });
var endOfFunction = environment.indexOf('return ENV;');
var addKeys = Object.keys(config.ENV);
var configToAddgString = '';
for (var i = 0, len = addKeys.length; i < len; i++) {
configToAddgString += `ENV[\'${addKeys[i]}\'] = ${JSON.stringify(config.ENV[addKeys[i]], null, 2)};\n\n`;
}
var newEnvironment = environment.slice(0, endOfFunction) + configToAddgString + environment.slice(endOfFunction);
return [environmentPath, newEnvironment];
};
================================================
FILE: src/tasks/generate/runMachine.js
================================================
var { spawn } = require('child-process-promise');
var npmInstall = require('enpeem').install;
var fs = require('fs-extra');
var path = require('path');
var chalk = require('chalk');
var log = require('captains-log')();
var dockerCompose = require('../../helpers/dockerCompose')();
var ember = require('../../helpers/ember');
var createEmberConfig = require('./createEmberConfig');
module.exports = {
/**
* @overview This function executes the machine that is responsible for running client tasks
*
* @param {String} blueprintPath - path to the blueprint/addon
* @param {Object} [options] - Options passed over by commander including all set flags such --verbose
* @return {Promise} promise - So we know when everything has been executed.
*/
client: function (blueprintPath, projectRoot, clientName, options) {
var blueprint = require(blueprintPath);
var promise = new Promise(function (resolve, reject) {
blueprint.client.exec({
success: async function (actions) {
var addonErrors = [];
// only install ember-addons addons if --skip-npm is not set
if (!options.skipNpm) {
for (let emberAddon of actions.addEmberAddons) {
var addonSemVer = emberAddon.name + '@' + emberAddon.target;
var emberArgs = ['install', addonSemVer];
try {
await spawn(ember, emberArgs, { stdio: 'inherit', cwd: path.join(projectRoot, clientName) });
} catch (error) {
console.log(chalk.red('Error with ember install.') + chalk.yellow(' Carrying on installation.'));
log.verbose(chalk.red(error.message));
if (error.stack) {
log.verbose(error.stack);
}
addonErrors.push(error);
}
}
}
for (var generate of actions.generates) {
emberArgs = ['generate', generate.type, generate.name, generate.parameters];
try {
await spawn(ember, emberArgs, { stdio: 'inherit', cwd: path.join(projectRoot, clientName) });
} catch (error) {
console.log(chalk.red('Error with ember generate.') + chalk.yellow(' Carrying on installation.'));
log.verbose(chalk.red(error.message));
if (error.stack) {
log.verbose(error.stack);
}
addonErrors.push(error);
}
}
// if addToConfig exists parse the ember-cli environment.js and add the new variables
// to the end of the function just before the return ENV; statement
// UNIT/ACCEPTANCE TEST THAT!
if (actions.addToConfig) {
var [environmentPath, newEnvironment] = createEmberConfig(projectRoot, actions.addToConfig);
fs.writeFileSync(environmentPath, newEnvironment);
}
if (addonErrors.length > 0) {
reject(addonErrors);
} else {
log.verbose('Client Machine Promise resolved.');
resolve();
}
}
});
});
return promise;
},
/**
* @overview This function executes the machine that is responsible for running server tasks
*
* @param {String} blueprintPath - path to the blueprint/addon
* @param {Object} [options] - Options passed over by commander including all set flags such --verbose
* @return {Promise} promise - So we know when everything has been executed.
*/
server: function (blueprintPath, serverName, options) {
var blueprint = require(blueprintPath);
var promise = new Promise(function (resolve, reject) {
blueprint.server.exec({
success: async function(actions) {
var packageSemVers = [];
var addonErrors = [];
for (var npmPackage of actions.addNpmPackages) {
packageSemVers.push(npmPackage.name + '@' + npmPackage.target);
}
if (packageSemVers.length > 0) {
var cmdPrefix = false;
var prefix = serverName;
if (options.docker) {
cmdPrefix = `${dockerCompose} run server`;
prefix = undefined;
}
var installOptions = {
dependencies: packageSemVers,
// skip: options.skip,
dryRun : options.skipNpm,
save : true,
// needed for optional docker
cmdPrefix : cmdPrefix,
// saves in the server file
prefix : prefix
};
try {
await npmInstall(installOptions);
} catch (error) {
addonErrors.push(error);
console.log(chalk.red('Error with server npm install.') + chalk.yellow(' Carrying on installation.'));
log.verbose(chalk.red(error.message));
if (error.stack) {
log.verbose(error.stack);
}
}
}
if (addonErrors.length > 0) {
reject(addonErrors);
} else {
log.verbose('Client Machine Promise resolved.');
resolve();
}
}
});
});
return promise;
}
};
================================================
FILE: src/tasks/getAppNames.js
================================================
'use strict';
/*
* IMPORTANT: Right now only one client and server are supported.
*
* Checks the .sane-cli files for registered server and client apps.
* Which is then uesd for logging and to identify the folder names
*
* Conventions used:
* Clients either start with client then the full name is taken for logging and the foldername,
* or they end with -client and then that is truncated and merely serves identification
*
* Servers follow the same convention, just starting with server or ending with -server
*
* Examples:
* 'client' => Expected folder-name: client
* 'clientv1' => Expected folder-name: clientv1
* 'admin-v1-client' => Expected folder-name: admin-v1
* 'server' => Expected folder-name: server
* 'server2' => Expected folder-name: server2
* 'api-v1-server' => Expected folder-name: api-v1
*/
var Yam = require('yam');
var apps = new Yam('sane-cli').get('apps');
var self = {
client: function () {
var client = self.getApp('client');
return client;
},
// only use this function for windows and mac
server: function () {
var server = self.getApp('server');
return server;
},
getApp: function (filter) {
if (apps === undefined) {
return filter;
}
var filteredApps = apps.filter(app => app.startsWith(filter) || app.endsWith(filter));
// var filteredApps = apps.filter(app => app.beginsWith(filter) || app.endsWith(filter));
if (filteredApps[0].length > (filter.length + 1) && filteredApps[0].endsWith(filter)) {
// slice off -client or -server at the end
filteredApps[0] = filteredApps[0].slice(0, -(filter.length + 1));
}
return filteredApps[0];
}
};
module.exports = self;
================================================
FILE: src/tasks/getTemplates.js
================================================
'use strict';
/*
* filters out templates in given folderpath, taking only files with given nameBegins
* and filtering out the ones that don't match nameEnds
* Currently only taking js filesi into consideration.
*/
var walkSync = require('walk-sync');
var path = require('path');
// require('es6-shim');
module.exports = function getTemplates(folderPath) {
folderPath = path.join(folderPath, 'templates');
var walked = walkSync(folderPath);
// Only leave files (plus their relative path) in the walked array
var walkedFiles = walked.filter(function (element){
return element.indexOf('.') > 0;
});
// remove any root files, since they will be copied over at the beginning
// NOTE(markus): Not sure if that works on Windows
walkedFiles = walkedFiles.filter(function (element){
return element.indexOf('/') > 0;
});
return {
root : folderPath,
templates: walkedFiles
};
};
================================================
FILE: src/tasks/modelConversion.js
================================================
'use strict';
/*
* Converts ember model attributes to sails model attributes and vice versa
* Sails supports: string, text, integer, float, date, datetime, boolean, binary, array, json
* Ember Data supports: string, number, boolean, and date
*/
module.exports = {
toEmber: function (attributes){
// create a deep copy so the original element does not get overwritten
var attributesCopy = attributes.slice(0);
for (var i in attributesCopy) {
var attrArray = attributesCopy[i].split(':');
// if the given attribute does not have a colon, ignore
if (attrArray.length > 1) {
// get the last element of the array
var attrType = attrArray.pop();
switch (attrType) {
case 'text':
attrArray.push('string');
break;
case 'integer':
attrArray.push('number');
break;
case 'float':
attrArray.push('number');
break;
case 'datetime':
attrArray.push('date');
break;
case 'binary':
attrArray.push('boolean');
break;
case 'array':
// no proper equivalent on the ember side
break;
case 'json':
// no proper equivalent on the ember side
break;
default:
// add popped element back
attrArray.push(attrType);
break;
}
attributesCopy[i] = attrArray.join(':');
}
}
return attributesCopy;
},
toSails: function (attributes){
// create a deep copy so the original element does not get overwritten
var attributesCopy = attributes.slice(0);
for (var i in attributesCopy) {
var attrArray = attributesCopy[i].split(':');
// if the given attribute does not have a colon, ignore
if (attrArray.length > 1) {
var attrType = attrArray.pop();
if (attrType === 'number') {
attrArray.push('float');
} else {
// add popped element back
attrArray.push(attrType);
}
attributesCopy[i] = attrArray.join(':');
}
}
return attributesCopy;
}
};
================================================
FILE: src/tasks/renameTemplate.js
================================================
'use strict';
/**
* @module sane
*/
/**
* Removes the .hbs extension from template files.
*
* @private
* @method renameTemplate
* @param {string} name Filename of the handlebars template file.
* @return {string} Filename without .hbs extension
*/
module.exports = function renameTemplate(name) {
return name.replace(/\.hbs$/, '');
};
================================================
FILE: src/tasks/trackCommand.js
================================================
'use strict';
/*
* Is used to track every typed in command with all the flags to GA.
*/
module.exports = function trackCommand(name, options, leek) {
var optionString = '';
for (var opt in options) {
// exclude properties that are not cli options
if (options.hasOwnProperty(opt) &&
!opt.startsWith('_') &&
['commands', 'options', 'parent'].indexOf(opt) === -1) {
optionString += `--${opt}=${options[opt]} `;
}
}
name = `${name} ${optionString}`;
leek.track({
name : 'sane ',
message: name
});
};
================================================
FILE: templates/.gitignore
================================================
node_modules
================================================
FILE: templates/client/app/adapters/application.js
================================================
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
coalesceFindRequests: true,
namespace: 'api/v1',
//this is dependent on production/development environment
//It is configured in config/environment.js
//host: ClientENV.hostUrl
//add IP from $DOCKER_HOST if --docker flag is set
//host: 'http://192.168.59.103:1337'
});
================================================
FILE: templates/client/app/serializers/application.js
================================================
import DS from 'ember-data';
export default DS.RESTSerializer.extend();
================================================
FILE: templates/docker-compose.yml.hbs
================================================
{{#if database}}
db:
image: {{database}}:latest #postgres, mysql, mongo, redis. You can also specifiy a specific version instead of using latest.
ports:
- "{{port}}:{{port}}" #for postgres "5432:5432", for mysql "3306:3306", for mongo "27017:27017", etc
{{#if isMysql}}
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=sane
{{/if}}
{{/if}}
{{serverName}}:
#Docker-sails supports several different tags:
#0.10.32/33/34 (node 0.10.32), stable (latest node 0.10.x), latest (latest node 0.11.x)
#0.10.32/33/34-pm2 (node 0.10.32), stable-pm2 (latest node 0.10.x), latest-pm2 (latest node 0.11.x)
image: artificial/docker-sails:stable-pm2
command: sails lift
volumes:
- "./{{serverName}}/:/{{serverName}}"
ports:
- "1337:1337"
{{#if user}}
user: "{{user}}"
{{/if}}
{{#if database}}
links:
- db
{{/if}}
#For a future version
#environment:
# - VIRTUAL_HOST=sails
# - VIRTUAL_PORT=1337
================================================
FILE: templates/fig.yml.hbs
================================================
{{#if database}}
db:
image: {{database}}:latest #postgres, mysql, mongo, redis. You can also specifiy a specific version instead of using latest.
ports:
- "{{port}}:{{port}}" #for postgres "5432:5432", for mysql "3306:3306", for mongo "27017:27017", etc
{{#if isMysql}}
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=sane
{{/if}}
{{/if}}
{{serverName}}:
#Docker-sails supports several different tags:
#0.10.32/33/34 (node 0.10.32), stable (latest node 0.10.x), latest (latest node 0.11.x)
#0.10.32/33/34-pm2 (node 0.10.32), stable-pm2 (latest node 0.10.x), latest-pm2 (latest node 0.11.x)
image: artificial/docker-sails:stable-pm2
command: sails lift
volumes:
- {{serverName}}/:/{{serverName}}
ports:
- "1337:1337"
{{#if user}}
user: {{user}}
{{/if}}
{{#if database}}
links:
- db
{{/if}}
#For a future version
#environment:
# - VIRTUAL_HOST=sails
# - VIRTUAL_PORT=1337
================================================
FILE: templates/package.json.hbs
================================================
{
"name": "{{name}}",
"version": "0.0.1",
"description": "A blank sane stack project.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"sane-cli": "{{version}}"
}
}
================================================
FILE: templates/server/.sailsrc
================================================
{
"hooks": {
"grunt": false,
"views": false
},
"paths": {
"public": "assets"
}
}
================================================
FILE: templates/server/api/controllers/AppController.js
================================================
/**
* AppController
*
* @description :: Server-side logic for managing apps
* @help :: See http://links.sailsjs.org/docs/controllers
*/
var fs = require('fs');
module.exports = {
/**
* `AppController.serve()`
* Serves your Ember App directly from the assets/index.html
*
* Add some custom code before delivering the app if you want
* You could add some analytics, or use this to serve different
* ember apps to differen people.
* That can be useful for limited feature roll-out or A/B Testing, etc.
*
*/
serve: function (req, res) {
var emberApp = __dirname + '/../../assets/index.html';
fs.exists(emberApp, function (exists) {
if (!exists) {
return res.notFound('The requested file does not exist.');
}
fs.createReadStream(emberApp).pipe(res);
});
}
};
================================================
FILE: templates/server/api/responses/badRequest.js
================================================
/**
* 400 (Bad Request) Handler
*
* Usage:
* return res.badRequest();
* return res.badRequest(data);
* return res.badRequest(data, 'some/specific/badRequest/view');
*
* e.g.:
* ```
* return res.badRequest(
* 'Please choose a valid `password` (6-12 characters)',
* 'trial/signup'
* );
* ```
*/
module.exports = function badRequest(data, options) {
// Get access to `req`, `res`, & `sails`
var req = this.req;
var res = this.res;
var sails = req._sails;
// Set status code
res.status(400);
// Log error to console
if (data !== undefined) {
sails.log.verbose('Sending 400 ("Bad Request") response: \n', data);
}
else sails.log.verbose('Sending 400 ("Bad Request") response');
// Only include errors in response if application environment
// is not set to 'production'. In production, we shouldn't
// send back any identifying information about errors.
if (sails.config.environment === 'production') {
data = undefined;
}
// If the user-agent wants JSON, always respond with JSON
return res.jsonx(data);
};
================================================
FILE: templates/server/api/responses/forbidden.js
================================================
/**
* 403 (Forbidden) Handler
*
* Usage:
* return res.forbidden();
* return res.forbidden(err);
* return res.forbidden(err, 'some/specific/forbidden/view');
*
* e.g.:
* ```
* return res.forbidden('Access denied.');
* ```
*/
module.exports = function forbidden (data, options) {
// Get access to `req`, `res`, & `sails`
var req = this.req;
var res = this.res;
var sails = req._sails;
// Set status code
res.status(403);
// Log error to console
if (data !== undefined) {
sails.log.verbose('Sending 403 ("Forbidden") response: \n', data);
}
else sails.log.verbose('Sending 403 ("Forbidden") response');
// Only include errors in response if application environment
// is not set to 'production'. In production, we shouldn't
// send back any identifying information about errors.
if (sails.config.environment === 'production') {
data = undefined;
}
// If the user-agent wants JSON, always respond with JSON
return res.jsonx(data);
};
================================================
FILE: templates/server/api/responses/notFound.js
================================================
/**
* 404 (Not Found) Handler
*
* Usage:
* return res.notFound();
* return res.notFound(err);
* return res.notFound(err, 'some/specific/notfound/view');
*
* e.g.:
* ```
* return res.notFound();
* ```
*
* NOTE:
* If a request doesn't match any explicit routes (i.e. `config/routes.js`)
* or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()`
* automatically.
*/
module.exports = function notFound (data, options) {
// Get access to `req`, `res`, & `sails`
var req = this.req;
var res = this.res;
var sails = req._sails;
// Set status code
res.status(404);
// Log error to console
if (data !== undefined) {
sails.log.verbose('Sending 404 ("Not Found") response: \n', data);
}
else sails.log.verbose('Sending 404 ("Not Found") response');
// Only include errors in response if application environment
// is not set to 'production'. In production, we shouldn't
// send back any identifying information about errors.
if (sails.config.environment === 'production') {
data = undefined;
}
// If the user-agent wants JSON, always respond with JSON
return res.jsonx(data);
};
================================================
FILE: templates/server/api/responses/ok.js
================================================
/**
* 200 (OK) Response
*
* Usage:
* return res.ok();
* return res.ok(data);
* return res.ok(data, 'auth/login');
*
* @param {Object} data
* @param {String|Object} options
* - pass string to render specified view
*/
module.exports = function sendOK (data, options) {
// Get access to `req`, `res`, & `sails`
var req = this.req;
var res = this.res;
var sails = req._sails;
sails.log.silly('res.ok() :: Sending 200 ("OK") response');
// Set status code
res.status(200);
return res.jsonx(data);
};
================================================
FILE: templates/server/api/responses/serverError.js
================================================
/**
* 500 (Server Error) Response
*
* Usage:
* return res.serverError();
* return res.serverError(err);
* return res.serverError(err, 'some/specific/error/view');
*
* NOTE:
* If something throws in a policy or controller, or an internal
* error is encountered, Sails will call `res.serverError()`
* automatically.
*/
module.exports = function serverError (data, options) {
// Get access to `req`, `res`, & `sails`
var req = this.req;
var res = this.res;
var sails = req._sails;
// Set status code
res.status(500);
// Log error to console
if (data !== undefined) {
sails.log.error('Sending 500 ("Server Error") response: \n', data);
}
else sails.log.error('Sending empty 500 ("Server Error") response');
// Only include errors in response if application environment
// is not set to 'production'. In production, we shouldn't
// send back any identifying information about errors.
if (sails.config.environment === 'production') {
data = undefined;
}
// If the user-agent wants JSON, always respond with JSON
return res.jsonx(data);
};
================================================
FILE: templates/server/assets/index.html
================================================
<h1>Welcome to your backend!<h1><br>
<h2>When deploying replace this with your great ember app. In the meantime access your api at 'api/v1/models' and your ember app at localhost:4200.</h1>
================================================
FILE: templates/server/config/autoreload.js.hbs
================================================
var path = require('path');
module.exports.autoreload = {
active: process.env.NODE_ENV === undefined || process.env.NODE_ENV ==='development',
//use polling to watch file changes
//slower but needed for the docker image
usePolling: {{ pollingWatch }},
// Set dirs to watch
dirs: [
path.resolve('api', 'blueprints'),
path.resolve('api', 'controllers'),
path.resolve('api', 'models'),
path.resolve('api', 'policies'),
path.resolve('api', 'responses'),
path.resolve('api', 'services'),
path.resolve('config')
]
};
================================================
FILE: templates/server/config/blueprints.js
================================================
/**
* Blueprint API Configuration
* (sails.config.blueprints)
*
* These settings are for the global configuration of blueprint routes and
* request options (which impact the behavior of blueprint actions).
*
* You may also override any of these settings on a per-controller basis
* by defining a '_config' key in your controller defintion, and assigning it
* a configuration object with overrides for the settings in this file.
* A lot of the configuration options below affect so-called "CRUD methods",
* or your controllers' `find`, `create`, `update`, and `destroy` actions.
*
* It's important to realize that, even if you haven't defined these yourself, as long as
* a model exists with the same name as the controller, Sails will respond with built-in CRUD
* logic in the form of a JSON API, including support for sort, pagination, and filtering.
*
* For more information on the blueprint API, check out:
* http://sailsjs.org/#/documentation/reference/blueprint-api
*
* For more information on the settings in this file, see:
* http://sailsjs.org/#/documentation/reference/sails.config/sails.config.blueprints.html
*
*/
module.exports.blueprints = {
/***************************************************************************
* *
* Action routes speed up the backend development workflow by *
* eliminating the need to manually bind routes. When enabled, GET, POST, *
* PUT, and DELETE routes will be generated for every one of a controller's *
* actions. *
* *
* If an `index` action exists, additional naked routes will be created for *
* it. Finally, all `actions` blueprints support an optional path *
* parameter, `id`, for convenience. *
* *
* `actions` are enabled by default, and can be OK for production-- *
* however, if you'd like to continue to use controller/action autorouting *
* in a production deployment, you must take great care not to *
* inadvertently expose unsafe/unintentional controller logic to GET *
* requests. *
* *
***************************************************************************/
// actions: true,
/***************************************************************************
* *
* RESTful routes (`sails.config.blueprints.rest`) *
* *
* REST blueprints are the automatically generated routes Sails uses to *
* expose a conventional REST API on top of a controller's `find`, *
* `create`, `update`, and `destroy` actions. *
* *
* For example, a BoatController with `rest` enabled generates the *
* following routes: *
* ::::::::::::::::::::::::::::::::::::::::::::::::::::::: *
* GET /boat/:id? -> BoatController.find *
* POST /boat -> BoatController.create *
* PUT /boat/:id -> BoatController.update *
* DELETE /boat/:id -> BoatController.destroy *
* *
* `rest` blueprint routes are enabled by default, and are suitable for use *
* in a production scenario, as long you take standard security precautions *
* (combine w/ policies, etc.) *
* *
***************************************************************************/
// rest: true,
/***************************************************************************
* *
* Shortcut routes are simple helpers to provide access to a *
* controller's CRUD methods from your browser's URL bar. When enabled, *
* GET, POST, PUT, and DELETE routes will be generated for the *
* controller's`find`, `create`, `update`, and `destroy` actions. *
* *
* `shortcuts` are enabled by default, but should be disabled in *
* production. *
* *
***************************************************************************/
// shortcuts: true,
/***************************************************************************
* *
* An optional mount path for all blueprint routes on a controller, *
* including `rest`, `actions`, and `shortcuts`. This allows you to take *
* advantage of blueprint routing, even if you need to namespace your API *
* methods. *
* *
* (NOTE: This only applies to blueprint autoroutes, not manual routes from *
* `sails.config.routes`) *
* *
***************************************************************************/
prefix: '/api/v1',
/***************************************************************************
* *
* Whether to pluralize controller names in blueprint routes. *
* *
* (NOTE: This only applies to blueprint autoroutes, not manual routes from *
* `sails.config.routes`) *
* *
* For example, REST blueprints for `FooController` with `pluralize` *
* enabled: *
* GET /foos/:id? *
* POST /foos *
* PUT /foos/:id? *
* DELETE /foos/:id? *
* *
***************************************************************************/
pluralize: true,
/***************************************************************************
* *
* Whether the blueprint controllers should populate model fetches with *
* data from other models which are linked by associations *
* *
* If you have a lot of data in one-to-many associations, leaving this on *
* may result in very heavy api calls *
* *
***************************************************************************/
// populate: true,
/****************************************************************************
* *
* Whether to run Model.watch() in the find and findOne blueprint actions. *
* Can be overridden on a per-model basis. *
* *
****************************************************************************/
// autoWatch: true,
/****************************************************************************
* *
* The default number of records to show in the response from a "find" *
* action. Doubles as the default size of populated arrays if populate is *
* true. *
* *
****************************************************************************/
// defaultLimit: 30
};
================================================
FILE: templates/server/config/connections.js.hbs
================================================
/**
* Connections
* (sails.config.connections)
*
* `Connections` are like "saved settings" for your adapters. What's the difference between
* a connection and an adapter, you might ask? An adapter (e.g. `sails-mysql`) is generic--
* it needs some additional information to work (e.g. your database host, password, user, etc.)
* A `connection` is that additional information.
*
* Each model must have a `connection` property (a string) which is references the name of one
* of these connections. If it doesn't, the default `connection` configured in `config/models.js`
* will be applied. Of course, a connection can (and usually is) shared by multiple models.
* .
* Note: If you're using version control, you should put your passwords/api keys
* in `config/local.js`, environment variables, or use another strategy.
* (this is to prevent you inadvertently sensitive credentials up to your repository.)
*
* For more information on configuration, check out:
* http://sailsjs.org/#/documentation/reference/sails.config/sails.config.connections.html
*/
module.exports.connections = {
/***************************************************************************
* *
* Local disk storage for DEVELOPMENT ONLY *
* *
* Installed by default. *
* *
***************************************************************************/
disk: {
adapter: 'sails-disk'
},
/***************************************************************************
* *
* MySQL is the world's most popular relational database. *
* http://en.wikipedia.org/wiki/MySQL *
* *
* Run: npm install sails-mysql *
* *
***************************************************************************/
//docker host uses db, otherwise use localhost or the IP of your db
//credentials for docker are defined in the fig.yml
//otherwise dependent on your setup
mysql: {
adapter: 'sails-mysql',
host: '{{host}}',
user: 'root',
password: 'password',
database: 'sane'
},
/***************************************************************************
* *
* MongoDB is the leading NoSQL database. *
* http://en.wikipedia.org/wiki/MongoDB *
* *
* Run: npm install sails-mongo *
* *
***************************************************************************/
//docker host uses db, otherwise use localhost or the IP of your db
//credentials for docker are defined in the fig.yml
//otherwise dependent on your setup
mongo: {
adapter: 'sails-mongo',
host: '{{host}}',
port: 27017,
// user: 'username',
// password: 'password',
// database: 'your_mongo_db_name_here'
},
/***************************************************************************
* *
* PostgreSQL is another officially supported relational database. *
* http://en.wikipedia.org/wiki/PostgreSQL *
* *
* Run: npm install sails-postgresql *
* *
***************************************************************************/
//docker host uses db, otherwise use localhost or the IP of your db
//credentials for docker are defined in the fig.yml
//otherwise dependent on your setup
postgresql: {
adapter: 'sails-postgresql',
host: '{{host}}',
user: 'postgres',
password: '',
database: 'postgres'
},
/***************************************************************************
* *
* Redis is an open source, BSD licensed, advanced key-value cache and *
* store. *
* *
* http://en.wikipedia.org/wiki/Redis *
* *
* Run: npm install sails-redis *
* *
***************************************************************************/
redis: {
adapter: 'sails-redis',
port: 6379,
host: '{{host}}',
database: null
}
/***************************************************************************
* *
* More adapters: https://github.com/balderdashy/sails *
* *
***************************************************************************/
};
================================================
FILE: templates/server/config/models.js.hbs
================================================
/**
* Default model configuration
* (sails.config.models)
*
* Unless you override them, the following properties will be included
* in each of your models.
*
* For more info on Sails models, see:
* http://sailsjs.org/#/documentation/concepts/ORM
*/
module.exports.models = {
/***************************************************************************
* *
* Your app's default connection. i.e. the name of one of your app's *
* connections (see `config/connections.js`) *
* *
***************************************************************************/
connection: '{{database}}',
/***************************************************************************
* *
* How and whether Sails will attempt to automatically rebuild the *
* tables/collections/etc. in your schema. *
* *
* See http://sailsjs.org/#/documentation/concepts/ORM/model-settings.html *
* *
***************************************************************************/
migrate: '{{migrateType}}' //for NoSQL this should be 'safe' and for postgres or MySQL 'alter'
};
================================================
FILE: templates/server/config/routes.js
================================================
/**
* Route Mappings
* (sails.config.routes)
*
* Your routes map URLs to views and controllers.
*
* If Sails receives a URL that doesn't match any of the routes below,
* it will check for matching files (images, scripts, stylesheets, etc.)
* in your assets directory. e.g. `http://localhost:1337/images/foo.jpg`
* might match an image file: `/assets/images/foo.jpg`
*
* Finally, if those don't match either, the default 404 handler is triggered.
* See `api/responses/notFound.js` to adjust your app's 404 logic.
*
* Note: Sails doesn't ACTUALLY serve stuff from `assets`-- the default Gruntfile in Sails copies
* flat files from `assets` to `.tmp/public`. This allows you to do things like compile LESS or
* CoffeeScript for the front-end.
*
* For more information on configuring custom routes, check out:
* http://sailsjs.org/#/documentation/concepts/Routes/RouteTargetSyntax.html
*/
module.exports.routes = {
/***************************************************************************
* *
* Make the view located at `views/homepage.ejs` (or `views/homepage.jade`, *
* etc. depending on your default view engine) your home page. *
* *
* (Alternatively, remove this and add an `index.html` file in your *
* `assets` directory) *
* *
***************************************************************************/
//'/': {
// view: 'homepage'
//}
/***************************************************************************
* *
* Custom routes here... *
* *
* If a request to a URL doesn't match any of the custom routes above, it *
* is matched against Sails route blueprints. See `config/blueprints.js` *
* for configuration options and examples. *
* *
***************************************************************************/
//This automatically serves all routes, apart from /api/** routes to ember
//(which will be initialized in assets/index.html). This route needs to be
//at the very bottom if you want to server other routes through Sails, because they are matched in order
'/*': { controller: 'App', action: 'serve', skipAssets: true, skipRegex: /^\/api\/.*$/ }
//You could also just serve the index view directly if you want
//'/*': { view: 'index', skipAssets: true, skipRegex: /^\/api\/.*$/ }
};
================================================
FILE: tests/.eslintrc
================================================
{
"env": {
"mocha": true
}
}
================================================
FILE: tests/acceptance/generateTest.js
================================================
'use strict';
var assertFile = require('../helpers/assertFile');
var fs = require('fs-extra');
var tmp = require('tmp-sync');
var { spawn } = require('child-process-promise');
var { initApp, sane, root, tmproot } = require('../helpers/acceptanceSetup');
describe('Acceptance: sane generate', function () {
var tmpdir;
beforeEach(function () {
tmpdir = tmp.in(tmproot);
process.chdir(tmpdir);
});
afterEach(function () {
process.chdir(root);
fs.removeSync(tmproot);
});
async function generate(args) {
await initApp();
var generateArgs = ['generate'].concat(args.split(' '));
var generateOpts = {
stdio: 'ignore'
};
return spawn(sane, generateArgs, generateOpts);
}
it('resource/api user', async function () {
await generate('resource user');
assertFile('client/app/models/user.js', {
contains: [
'import DS from \'ember-data\';',
'export default DS.Model.extend'
]
});
});
it('resource/api user name-string age-number', async function () {
await generate('resource user name:string age:number');
assertFile('client/app/models/user.js', {
contains: [
'import DS from \'ember-data\';',
'name: DS.attr(\'string\'),',
'age: DS.attr(\'number\')'
]
});
});
/*
we expect that all:
server models will be singular
server controllers will be singular
ember models will be singular
ember routes will be whatever was passed in
*/
// Assert that the proper filenames are generated in
// client when singular format is used
// (e.g. sane generate resource user)
// ()
// server when singular format is used (e.g. sane generate resource user)
// client when plural format is used (e.g. sane generate resource users)
// server when plural format is used (e.g. sane generate resource users)
// test model resource creation
it('should generate a singular sails model when passed a singular value', async function () {
await generate('resource foo');
assertFile('server/api/models/Foo.js');
});
it('should generate a singular sails model when passed a plural value', async function () {
await generate('resource bars');
assertFile('server/api/models/Bar.js');
});
it('should generate a singular ember model when passed a singular value', async function () {
await generate('resource foo');
assertFile('client/app/models/foo.js');
});
it('should generate a singular ember model when passed a plural value', async function () {
await generate('resource bars');
assertFile('client/app/models/bar.js');
});
// test controller creation
it('should generate a singular sails controller when passed a singular value', async function () {
await generate('resource foo');
assertFile('server/api/controllers/FooController.js');
});
it('should generate a singular sails controller when passed a plural value', async function () {
await generate('resource bars');
assertFile('server/api/controllers/BarController.js');
});
// test ember route createion
it('should generate a singular ember route when passed a singular value', async function () {
await generate('resource foo');
assertFile('client/app/routes/foo.js');
});
it('should generate a plural ember route when passed a plural value', async function () {
await generate('resource bars');
assertFile('client/app/routes/bars.js');
});
// test ember template creation
it('should generate a singular ember template when passed a singular value', async function () {
await generate('resource foo');
assertFile('client/app/templates/foo.hbs');
});
it('should generate a plural ember template when passed a plural value', async function () {
await generate('resource bars');
assertFile('client/app/templates/bars.hbs');
});
// start of test to validate the sails models and controllers are created as expected.
//
// it('resource/api person name:string age:number (server)', async function () {
// await generate('resource person name:string age:number');
//
// assertFile('server/api/models/Users.js', {
// contains: [
// 'module.exports = {',
// 'name : { type: \'string\' },',
// 'age : { type: \'float\' },'
// ]
// });
// });
});
================================================
FILE: tests/acceptance/helpTest.js
================================================
/*eslint-env node, mocha, es6 */
'use strict';
var { expect } = require('chai');
var { execFile } = require('child-process-promise');
var { sane } = require('../helpers/acceptanceSetup');
var version = require('../../package.json').version;
describe('Acceptance: sane help', function () {
it('displays commands, it\'s aliases and the correct cli version', async function () {
var output = await execFile(sane, ['help']);
output = output.stdout;
expect(output).to.include('new|n');
expect(output).to.include('up|serve');
expect(output).to.include('generate|g');
expect(output).to.include('version: ' + version);
});
});
================================================
FILE: tests/acceptance/installTest.js
================================================
'use strict';
var assertFile = require('../helpers/assertFile');
var fs = require('fs-extra');
var path = require('path');
var tmp = require('tmp-sync');
// var { execFile } = require('child-process-promise');
var { spawn } = require('child-process-promise');
var { initApp, sane, root, tmproot } = require('../helpers/acceptanceSetup');
describe('Acceptance: sane install', function () {
var tmpdir;
beforeEach(function () {
tmpdir = tmp.in(tmproot);
process.chdir(tmpdir);
});
afterEach(function () {
process.chdir(root);
fs.removeSync(tmproot);
});
async function install(args) {
var installArgs = ['install'].concat(args.split(' '));
await initApp();
// to see output run spawn with { stdio: 'inherit' }
return spawn(sane, installArgs, { stdio: 'inherit' });
// return execFile(sane, installArgs);
}
// Note especially check the addToConfig functionality!
it('sane-auth and runs generator', async function () {
await install('sane-auth --verbose --force --skip-npm');
// checks that addon has been installed
assertFile(path.join('node_modules', 'sane-auth', 'package.json'));
// check that it also got save-deved
assertFile('package.json', {
contains: [
'devDependencies',
'"sane-auth": '
]
});
// checks that templates have been copied over properly from the generator
assertFile(path.join('server', 'api', 'policies', 'hasToken.js'));
assertFile(path.join('server', 'package.json'), {
contains: [
'dependencies',
'jsonwebtoken'
]
});
assertFile(path.join('client', 'app', 'models', 'user.js'));
});
});
================================================
FILE: tests/acceptance/newTest.js
================================================
/*eslint-env node, mocha, es6 */
'use strict';
var fs = require('fs-extra');
var path = require('path');
var tmp = require('tmp-sync');
var dockerCompose = require('../../lib/helpers/dockerCompose')();
var assertFile = require('../helpers/assertFile');
var assertFileEquals = require('../helpers/assertFileEquals');
var { initApp, tmproot, root } = require('../helpers/acceptanceSetup');
var root = process.cwd();
var tmproot = path.join(root, 'tmp');
describe('Acceptance: sane new', function () {
var tmpdir;
beforeEach(function () {
tmpdir = tmp.in(tmproot);
process.chdir(tmpdir);
});
afterEach(function () {
process.chdir(root);
fs.removeSync(tmproot);
});
it('sane new . in empty folder works and adds specified dependencies to server package.json', async function () {
await initApp();
// taken from lib/commands/new.js
var sailsPackages = ['sails-generate-ember-blueprints', 'lodash', 'sails-hook-autoreload',
'pluralize', 'sails-disk'];
assertFile('server/package.json', {
contains: sailsPackages
});
});
it(`sane new facebook -d postgres, where facebook does not yet exist, works and adds settings to ${dockerCompose}.yml`, async function () {
await initApp([
'new',
'facebook',
'--skip-npm',
'--skip-bower',
'-d',
'postgres'
]);
process.chdir('facebook');
var expectedDocker = path.join(__dirname, '../fixtures/new/acceptance-test-docker-expected.yml');
var expectedConfig = path.join(__dirname, '../fixtures/new/acceptance-test-sane-cli-expected.js');
assertFileEquals(`${dockerCompose}.yml`, expectedDocker);
assertFileEquals('.sane-cli', expectedConfig);
});
it('sane new myspace -d redis, where myspace does not exist, works and creates myspace with redis', async function () {
await initApp([
'new',
'myspace',
'--skip-npm',
'--skip-bower',
'-d',
'redis'
]);
process.chdir('myspace');
assertFile('.sane-cli');
});
// function confirmBlueprintedForDir(dir) {
// return function() {
// var blueprintPath = path.join(root, dir, 'files');
// var expected = walkSync(blueprintPath);
// var actual = walkSync('.').sort();
// var folder = path.basename(process.cwd());
// forEach(Blueprint.renamedFiles, function(destFile, srcFile) {
// expected[expected.indexOf(srcFile)] = destFile;
// });
// expected.sort();
// assert.equal(folder, 'foo');
// assert.deepEqual(expected, actual, EOL + ' expected: ' + util.inspect(expected) +
// EOL + ' but got: ' + util.inspect(actual));
// };
// }
// function confirmBlueprinted() {
// return confirmBlueprintedForDir('blueprints/app');
// }
// it('ember new foo, where foo does not yet exist, works', function() {
// return ember([
// 'new',
// 'foo',
// '--skip-npm',
// '--skip-bower'
// ]).then(confirmBlueprinted);
// });
// it('ember new with empty app name doesnt throw exception', function() {
// return ember([
// 'new',
// ''
// ]);
// });
// it('ember new without app name doesnt throw exception', function() {
// return ember([
// 'new'
// ]);
// });
// it('ember new with app name creates new directory and has a dasherized package name', function() {
// return ember([
// 'new',
// 'FooApp',
// '--skip-npm',
// '--skip-bower',
// '--skip-git'
// ]).then(function() {
// assert(!fs.existsSync('FooApp'));
// var pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
// assert.equal(pkgJson.name, 'foo-app');
// });
// });
// it('Cannot run ember new, inside of ember-cli project', function() {
// return ember([
// 'new',
// 'foo',
// '--skip-npm',
// '--skip-bower',
// '--skip-git'
// ]).then(function() {
// return ember([
// 'new',
// 'foo',
// '--skip-npm',
// '--skip-bower',
// '--skip-git'
// ]).then(function() {
// assert(!fs.existsSync('foo'));
// });
// }).then(confirmBlueprinted);
// });
// it('ember new with blueprint uses the specified blueprint directory', function() {
// return tmp.setup('./tmp/my_blueprint')
// .then(function() {
// return tmp.setup('./tmp/my_blueprint/files');
// })
// .then(function() {
// fs.writeFileSync('./tmp/my_blueprint/files/gitignore');
// process.chdir('./tmp');
// return ember([
// 'new',
// 'foo',
// '--skip-npm',
// '--skip-bower',
// '--skip-git',
// '--blueprint=my_blueprint'
// ]);
// })
// .then(confirmBlueprintedForDir('tmp/my_blueprint'));
// });
// it('ember new with git blueprint uses checks out the blueprint and uses it', function(){
// this.timeout(10000);
// return ember([
// 'new',
// 'foo',
// '--skip-npm',
// '--skip-bower',
// '--skip-git',
// '--blueprint=https://github.com/trek/app-blueprint-test.git'
// ]).then(function() {
// assert(fs.existsSync('.ember-cli'));
// });
// });
// it('ember new without skip-git flag creates .git dir', function(){
// return ember([
// 'new',
// 'foo',
// '--skip-npm',
// '--skip-bower'
// ]).then(function() {
// assert(fs.existsSync('.git'));
// });
// });
// it('ember new with --dry-run does not create new directory', function(){
// return ember([
// 'new',
// 'foo',
// '--dry-run'
// ]).then(function(){
// var cwd = process.cwd();
// assert(!cwd.match('foo'), 'does not change cwd to foo in a dry run');
// assert(!fs.existsSync(path.join(cwd, 'foo')), 'does not create new directory');
// assert(!fs.existsSync(path.join(cwd, '.git')), 'does not create git in current directory');
// });
// });
});
================================================
FILE: tests/factories/.gitkeep
================================================
================================================
FILE: tests/fixtures/.gitkeep
================================================
================================================
FILE: tests/fixtures/generate/ember/acceptance-test-ember-user-expected.js
================================================
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
age: DS.attr('number')
});
================================================
FILE: tests/fixtures/generate/ember/acceptance-test-empty-user-expected.js
================================================
import DS from 'ember-data';
export default DS.Model.extend({
});
================================================
FILE: tests/fixtures/new/acceptance-test-docker-expected.yml
================================================
db:
image: postgres:latest #postgres, mysql, mongo, redis. You can also specifiy a specific version instead of using latest.
ports:
- "5432:5432" #for postgres "5432:5432", for mysql "3306:3306", for mongo "27017:27017", etc
server:
#Docker-sails supports several different tags:
#0.10.32/33/34 (node 0.10.32), stable (latest node 0.10.x), latest (latest node 0.11.x)
#0.10.32/33/34-pm2 (node 0.10.32), stable-pm2 (latest node 0.10.x), latest-pm2 (latest node 0.11.x)
image: artificial/docker-sails:stable-pm2
command: sails lift
volumes:
- server/:/server
ports:
- "1337:1337"
links:
- db
#For a future version
#environment:
# - VIRTUAL_HOST=sails
# - VIRTUAL_PORT=1337
================================================
FILE: tests/fixtures/new/acceptance-test-sane-cli-expected.js
================================================
{
"apps": [
"client",
"server"
],
"disableAnalytics": false,
"database": "postgresql",
"docker": false,
"verbose": false,
"skipNpm": true,
"skipBower": true
}
================================================
FILE: tests/helpers/.gitkeep
================================================
================================================
FILE: tests/helpers/acceptanceSetup.js
================================================
'use strict';
// var { execFile } = require('child-process-promise');
var { spawn } = require('child-process-promise');
var path = require('path');
var root = process.cwd();
var saneExec = (process.platform === 'win32' ? 'sane.cmd' : 'sane');
var self = {
root: root,
tmproot: path.join(root, 'tmp'),
sane: path.join(root, 'bin', saneExec),
// TODO(Markus): Try to remove execFile/exec and call the cli directly, promisified
initApp: function (args) {
args = args || ['new', '.', '--skip-npm', '--skip-bower', '--skip-analytics', '--verbose'];
// Note: execFile is slightly more efficient; Might have to use spawn on windows with var opts = { stdio: 'ignore' };
return spawn(self.sane, args, { stdio: 'inherit' });
// return execFile(self.sane, args);
}
};
module.exports = self;
================================================
FILE: tests/helpers/assertFile.js
================================================
'use strict';
var { expect } = require('chai');
var includes = require('lodash/collection/includes');
var flatten = require('lodash/array/flatten');
var fs = require('fs-extra');
var path = require('path');
var EOL = require('os').EOL;
/*
* File originally taken from ember-cli code at:
* https://github.com/ember-cli/ember-cli/edit/master/tests/helpers/assert-file.js
Asserts that a given file exists.
```js
assertFile('some/file.js');
```
You can also make assertions about the file's contents using
`contains` and `doesNotContain`:
```js
assertFile('some/file.js', {
contains: [
'foo',
/[0-9]+/
],
doesNotContain: 'bar'
});
```
@method assertFile
@param {String} file
@param {Object} options
Optional extra assertions to perform on the file.
@param {String, Array} options.contains
Strings or regular expressions the file must contain.
@param {String, Array} options.doesNotContain
Strings or regular expressions the file must *not* contain.
*/
module.exports = function assertFile(file, options) {
var filePath = path.join(process.cwd(), file);
expect(fs.existsSync(filePath)).to.equal(true, 'expected ' + file + ' to exist');
if (!options) {
return;
}
var actual = fs.readFileSync(filePath, { encoding: 'utf-8' });
if (options.contains) {
flatten([options.contains]).forEach(function (expected) {
var pass;
if (expected.test) {
pass = expected.test(actual);
} else {
pass = includes(actual, expected);
}
expect(pass).to.equal(true, EOL + EOL + 'expected ' + file + ':' + EOL + EOL +
actual +
EOL + 'to contain:' + EOL + EOL +
expected + EOL);
});
}
if (options.doesNotContain) {
flatten([options.doesNotContain]).forEach(function (unexpected) {
var pass;
if (unexpected.test) {
pass = !unexpected.test(actual);
} else {
pass = !includes(actual, unexpected);
}
expect(pass).to.equal(true, EOL + EOL + 'expected ' + file + ':' + EOL + EOL +
actual + EOL +
'not to contain:' + EOL + EOL +
unexpected + EOL);
});
}
};
================================================
FILE: tests/helpers/assertFileEquals.js
================================================
'use strict';
var { expect } = require('chai');
var fs = require('fs');
/*
Assert that a given file matches another.
@method assertFileEqual
@param {String} pathToActual
@param {String} pathToExpected
*/
module.exports = function assertFileEquals(pathToActual, pathToExpected) {
// Strip leading and trailing whitespace from files
// to avoid problems with some OS adding newlines at end of files
var actual = fs.readFileSync(pathToActual, { encoding: 'utf-8' }).toString().replace(/^(\s*)((\S+\s*?)*)(\s*)$/, '$2');
var expected = fs.readFileSync(pathToExpected, { encoding: 'utf-8' }).toString().replace(/^(\s*)((\S+\s*?)*)(\s*)$/, '$2');
expect(actual).to.equal(expected);
};
================================================
FILE: tests/helpers/killCliProcess.js
================================================
'use strict';
module.exports = function (childProcess) {
if (process.platform === 'win32') {
childProcess.send({kill: true});
} else {
childProcess.kill('SIGINT');
}
};
================================================
FILE: tests/helpers/promise.js
================================================
'use strict';
var RSVP = require('rsvp');
var Promise = RSVP.Promise;
module.exports = PromiseExt;
// Utility functions on the native CTOR need some massaging
module.exports.hash = function () {
return this.resolve(RSVP.hash.apply(null, arguments));
};
module.exports.denodeify = function () {
var fn = RSVP.denodeify.apply(null, arguments);
var Constructor = this;
var newFn = function () {
return Constructor.resolve(fn.apply(null, arguments));
};
Object.setPrototypeOf(newFn, arguments[0]);
return newFn;
};
module.exports.filter = function () {
return this.resolve(RSVP.filter.apply(null, arguments));
};
module.exports.map = function () {
return this.resolve(RSVP.map.apply(null, arguments));
};
function PromiseExt(resolver, label) {
this._superConstructor(resolver, label);
}
PromiseExt.prototype = Object.create(Promise.prototype);
PromiseExt.prototype.constructor = PromiseExt;
PromiseExt.prototype._superConstructor = Promise;
Object.setPrototypeOf(PromiseExt, Promise);
PromiseExt.prototype.returns = function (value) {
return this.then(function () {
return value;
});
};
PromiseExt.prototype.invoke = function (method) {
var args = Array.prototype.slice(arguments, 1);
return this.then(function (value) {
return value[method].apply(value, args);
}, undefined, 'invoke: ' + method + ' with: ' + args);
};
PromiseExt.prototype.map = function (mapFn) {
var Constructor = this.constructor;
return this.then(function (values) {
return Constructor.map(values, mapFn);
});
};
PromiseExt.prototype.filter = function (mapFn) {
var Constructor = this.constructor;
return this.then(function (values) {
return Constructor.filter(values, mapFn);
});
};
================================================
FILE: tests/helpers/runCommand.js
================================================
'use strict';
var RSVP = require('rsvp');
var chalk = require('chalk');
var spawn = require('child_process').spawn;
var defaults = require('lodash/object/defaults');
var killCliProcess = require('./killCliProcess');
var Promise = require('./promise');
module.exports = function run(/* command, args, options */) {
var command = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
var options = {};
if (typeof args[args.length - 1] === 'object') {
options = args.pop();
}
options = defaults(options, {
verbose: true,
onOutput: function (string) {
if (options.verbose) {
console.log(string);
}
},
onError: function (string) {
if (options.verbose) {
console.error(chalk.red(string));
}
}
});
return new RSVP.Promise(function (resolve, reject) {
console.log(' Running: ' + command + ' ' + args.join(' '));
var opts = {};
if (process.platform === 'win32') {
args = ['"' + command + '"'].concat(args);
command = 'node';
opts.windowsVerbatimArguments = true;
opts.stdio = [null, null, null, 'ipc'];
}
var child = spawn(command, args, opts);
var result = {
output: [],
errors: [],
code : null
};
if (options.onChildSpawned) {
var onChildSpawnedPromise = new Promise(function (childSpawnedResolve, childSpawnedReject) {
try {
options.onChildSpawned(child).then(childSpawnedResolve, childSpawnedReject);
} catch (err) {
childSpawnedReject(err);
}
});
onChildSpawnedPromise
.then(function () {
if (options.killAfterChildSpawnedPromiseResolution) {
killCliProcess(child);
}
}, function (err) {
result.testingError = err;
if (options.killAfterChildSpawnedPromiseResolution) {
killCliProcess(child);
}
});
}
child.stdout.on('data', function (data) {
var string = data.toString();
options.onOutput(string, child);
result.output.push(string);
});
child.stderr.on('data', function (data) {
var string = data.toString();
options.onError(string, child);
result.errors.push(string);
});
child.on('close', function (code) {
result.code = code;
if (code === 0) {
resolve(result);
} else {
reject(result);
}
});
});
};
================================================
FILE: tests/helpers/sane.js
================================================
'use strict';
var path = require('path');
var root = process.cwd();
var saneExec = (process.platform === 'win32' ? 'sane.cmd' : 'sane');
var sane = path.join(root, 'bin', saneExec);
module.exports = sane;
================================================
FILE: tests/runner.js
================================================
/*eslint no-process-exit:0*/
'use strict';
var glob = require('glob');
var Mocha = require('mocha');
var chalk = require('chalk');
require('babel-register');
require("babel-polyfill");
var mocha = new Mocha({
// For some reason, tests take a long time on Windows (or at least AppVeyor)
timeout : (process.platform === 'win32') ? 150000 : 40000,
reporter: 'spec'
});
// Determine which tests to run based on argument passed to runner
var arg = process.argv[2];
var root;
if (!arg) {
root = 'tests/{unit,acceptance}';
} else if (arg === 'unit') {
root = 'tests/unit';
} else {
root = 'tests/{unit,acceptance}';
}
function addFiles(mocha, files) {
glob.sync(root + files).forEach(mocha.addFile.bind(mocha));
}
addFiles(mocha, '/**/*Test.js');
// addFiles(mocha, '/**/getAddonTest.js');
mocha.run(function (failures) {
process.on('exit', function () {
if (failures === 1) {
console.log(chalk.red('1 Failing Test'));
} else if (failures > 1) {
console.log(chalk.red(failures, 'Failing Tests'));
} else if (failures === 0) {
console.log(chalk.green('All Tests Passed!'));
}
process.exit(failures);
});
});
================================================
FILE: tests/unit/analyticsTest.js
================================================
// 'use strict';
// var assert = require('../helpers/assert');
// var Command = require('../../lib/models/command');
// var MockUI = require('../helpers/mock-ui');
// var command;
// var called = false;
// beforeEach(function() {
// var analytics = {
// track: function() {
// called = true;
// }
// };
// var FakeCommand = Command.extend({
// name: 'fake-command',
// run: function() {}
// });
// command = new FakeCommand({
// ui: new MockUI(),
// analytics: analytics,
// project: { isEmberCLIProject: function(){ return true; }}
// });
// });
// afterEach(function() {
// command = null;
// });
// describe('analytics', function() {
// it('track gets invoked on command.validateAndRun()', function() {
// return command.validateAndRun([]).then(function() {
// assert.ok(called, 'expected analytics.track to be called');
// });
// });
// });
================================================
FILE: tests/unit/commands/generateTest.js
================================================
// 'use strict';
// var GenerateCommand = require('../../../lib/commands/generate');
// var Promise = require('../../../lib/ext/promise');
// var Task = require('../../../lib/models/task');
// var assert = require('../../helpers/assert');
// var commandOptions = require('../../factories/command-options');
// describe('generate command', function() {
// var command;
// beforeEach(function() {
// command = new GenerateCommand(commandOptions({
// settings: {},
// project: {
// name: function() {
// return 'some-random-name';
// },
// isEmberCLIProject: function isEmberCLIProject() {
// return true;
// }
// },
// tasks: {
// GenerateFromBlueprint: Task.extend({
// run: function(options) {
// return Promise.resolve(options);
// }
// })
// }
// }));
// });
// it('runs GenerateFromBlueprint with expected options', function() {
// return command.validateAndRun(['controller', 'foo'])
// .then(function(options) {
// assert.equal(options.pod, false);
// assert.equal(options.dryRun, false);
// assert.equal(options.verbose, false);
// assert.deepEqual(options.args, ['controller', 'foo']);
// });
// });
// it('complains if no blueprint name is given', function() {
// return command.validateAndRun([])
// .then(function() {
// assert.ok(false, 'should not have called run');
// })
// .catch(function(error) {
// assert.equal(error.message,
// 'The `ember generate` command requires a ' +
// 'blueprint name to be specified. ' +
// 'For more details, use `ember help`.');
// });
// });
// });
================================================
FILE: tests/unit/commands/newTest.js
================================================
/*eslint-env node, mocha, es6 */
'use strict';
var { expect } = require('chai');
var newCommand = require('../../../lib/commands/new');
describe('new command', function () {
var options; // , command
beforeEach(function () {
options = {
database: 'disk'
};
});
it('doesn\'t allow to create an application with a period in the name', async function() {
try {
await newCommand('i.love.dots', options);
expect(false, 'should have rejected with period in the application name');
} catch (error) {
expect(error.message).to.equal(`Sane currently does not support a projectname of 'i.love.dots'.`);
}
});
// it('doesn\'t allow to create an application with a name beginning with a number', function() {
// return command.validateAndRun(['123-my-bagel']).then(function() {
// assert.ok(false, 'should have rejected with a name beginning with a number');
// })
// .catch(function(error) {
// assert.equal(error.message, 'We currently do not support a name of `123-my-bagel`.');
// });
// });
// it('shows a suggestion messages when the application name is a period', function() {
// return command.validateAndRun(['.']).then(function() {
// assert.ok(false, 'should have rejected with a name `.`');
// })
// .catch(function(error) {
// assert.equal(error.message, 'Trying to generate an application structure on this folder? Use `ember init` instead.');
// });
// });
});
================================================
FILE: tests/unit/getAddonTest.js
================================================
'use strict';
var { expect } = require('chai');
// var Project = require('../../lib/tasks/Project');
// var pluck = require('lodash/collection/pluck');
// var path = require('path');
// var { spawn } = require('child_process');
// var installNpmPackage = require('../../lib/tasks/installNpmPackage');
// var getTemplates = require('../../lib/tasks/getTemplates');
// var copyToProject = require('../../lib/tasks/copyToProject');
// var fs = require('fs-extra');
describe('getAddon tests', async function() {
it('find closest project', async function() {
// var project = await Project.closest('/Users/markus/Projects/Artificial/moneyflow');
// console.log(project);
// var addons = project.discoverAddons();
// console.log(addons);
// var supportedBlueprints = pluck(addons, 'name');
// console.log(project.getBlueprintPath(supportedBlueprints[0]));
// var blueprintPath = project.getBlueprintPath(supportedBlueprints[0]);
// console.log(blueprintPath);
//
// var blueprint = require(blueprintPath);
// blueprint.client.exec({
// success: function(actions) {
// console.log('Client:');
// console.log(actions.addNpmPackages);
// console.log(actions.addEmberAddons);
// for (var emberAddon of actions.addEmberAddons) {
// // console.log(emberAddon);
// var addonSemVer = emberAddon.name + '@' + emberAddon.target
// var emberArgs = ['install:addon', addonSemVer]
// // console.log(emberArgs);
// spawn('ember', emberArgs, { stdio: 'inherit', cwd: path.join(project.root, 'client') });
// }
//
// }
// });
//
// blueprint.server.exec({
// success: function(actions) {
// console.log('Server!');
// console.log(actions.addNpmPackages);
// var serverPrefix = path.join(project.root, 'server');
// for (var npmPackage of actions.addNpmPackages) {
// // var emberArgs = ['install', npmPackage, '']
// // console.log(npmPackage.name + '@' + npmPackage.target)
// var packageSemVer = npmPackage.name + '@' + npmPackage.target
// installNpmPackage([packageSemVer], { save: true, prefix: serverPrefix }).then(function(result){
// console.log(result + 'has been installed');
// });
// }
// }
// });
//
// //now copy over templates
// var templates = getTemplates(path.join(blueprintPath, 'generate'));
// // console.log(templates);
// console.log('Copy over templates...');
// copyToProject(templates, project.root);
// console.log('done');
// fs.outputFileSync(templateOutPath, fs.readFileSync(templateInPath));
// // console.log();
// console.log(addons.pkg);
expect('1').to.equal('1');
});
});
================================================
FILE: tests/unit/helpers/emberTest.js
================================================
'use strict';
var ember = require('../../../lib/helpers/ember');
var expect = require('chai').expect;
describe('Unit: ember helper', function () {
it('returns a value', function () {
expect(ember).to.be.ok; // eslint-disable-line no-unused-expressions
});
it('returns local ember by default', function () {
expect(ember).to.match(/sane[\/\\]*node_modules[\/\\]*\.bin[\/\\]*ember(.cmd)?$/);
});
});
================================================
FILE: tests/unit/tasks/createEmberConfigTest.js
================================================
'use strict';
var { expect } = require('chai');
// var createEmberConfig = require('../../../lib/tasks/generate/createEmberConfig');
describe('createEmberConfig tests', async function() {
it('creates the correct config', async function() {
// NOTE (markus): Create a fixture ember-cli config file, then give new configuration
// to add and check that the path as well as the config returned are correct!
expect('1').to.equal('1');
});
});
gitextract_bwpiq3rl/
├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .sane-cli
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── appveyor.yml
├── bin/
│ ├── sane
│ └── sane.cmd
├── ecosystem.json
├── package.json
├── src/
│ ├── cli.js
│ ├── commands/
│ │ ├── generate.js
│ │ ├── index.js
│ │ ├── install.js
│ │ ├── new.js
│ │ ├── sync.js
│ │ └── up.js
│ ├── helpers/
│ │ ├── dockerCompose.js
│ │ ├── ember.js
│ │ ├── npm.js
│ │ ├── promise.js
│ │ ├── sails.js
│ │ └── setUpTracking.js
│ ├── projectMeta.js
│ └── tasks/
│ ├── Project.js
│ ├── checkEnvironment.js
│ ├── copyToProject.js
│ ├── generate/
│ │ ├── createEmberConfig.js
│ │ └── runMachine.js
│ ├── getAppNames.js
│ ├── getTemplates.js
│ ├── modelConversion.js
│ ├── renameTemplate.js
│ └── trackCommand.js
├── templates/
│ ├── .gitignore
│ ├── client/
│ │ └── app/
│ │ ├── adapters/
│ │ │ └── application.js
│ │ └── serializers/
│ │ └── application.js
│ ├── docker-compose.yml.hbs
│ ├── fig.yml.hbs
│ ├── package.json.hbs
│ └── server/
│ ├── .sailsrc
│ ├── api/
│ │ ├── controllers/
│ │ │ └── AppController.js
│ │ └── responses/
│ │ ├── badRequest.js
│ │ ├── forbidden.js
│ │ ├── notFound.js
│ │ ├── ok.js
│ │ └── serverError.js
│ ├── assets/
│ │ └── index.html
│ └── config/
│ ├── autoreload.js.hbs
│ ├── blueprints.js
│ ├── connections.js.hbs
│ ├── models.js.hbs
│ └── routes.js
└── tests/
├── .eslintrc
├── acceptance/
│ ├── generateTest.js
│ ├── helpTest.js
│ ├── installTest.js
│ └── newTest.js
├── factories/
│ └── .gitkeep
├── fixtures/
│ ├── .gitkeep
│ ├── generate/
│ │ └── ember/
│ │ ├── acceptance-test-ember-user-expected.js
│ │ └── acceptance-test-empty-user-expected.js
│ └── new/
│ ├── acceptance-test-docker-expected.yml
│ └── acceptance-test-sane-cli-expected.js
├── helpers/
│ ├── .gitkeep
│ ├── acceptanceSetup.js
│ ├── assertFile.js
│ ├── assertFileEquals.js
│ ├── killCliProcess.js
│ ├── promise.js
│ ├── runCommand.js
│ └── sane.js
├── runner.js
└── unit/
├── analyticsTest.js
├── commands/
│ ├── generateTest.js
│ └── newTest.js
├── getAddonTest.js
├── helpers/
│ └── emberTest.js
└── tasks/
└── createEmberConfigTest.js
SYMBOL INDEX (18 symbols across 10 files)
FILE: src/commands/new.js
function normalizeOption (line 25) | function normalizeOption(option) {
function prepareTemplate (line 36) | function prepareTemplate(name, variables) {
function prepareDockerTemplate (line 42) | function prepareDockerTemplate(name, option, user) {
function dockerExec (line 86) | function dockerExec(cmd, runsWithDocker, silent, dryRun) {
function getMigrateType (line 143) | function getMigrateType(database) {
function isWrongProjectName (line 163) | function isWrongProjectName(name) {
function cleanUpSails (line 170) | function cleanUpSails(options, silent){
FILE: src/commands/up.js
function printLines (line 26) | function printLines(data, prepend, color, outputMode) {
function runAndOutput (line 47) | function runAndOutput(cmd, parameters, execOptions, printOptions) {
FILE: src/helpers/promise.js
function PromiseExt (line 32) | function PromiseExt(resolver, label) {
FILE: src/helpers/setUpTracking.js
function clientId (line 6) | function clientId() {
FILE: src/tasks/Project.js
function closestPackageJSON (line 11) | async function closestPackageJSON(pathName) {
function Project (line 27) | function Project(root, pkg) {
FILE: src/tasks/copyToProject.js
function highlightDiff (line 11) | function highlightDiff(line) {
FILE: tests/acceptance/generateTest.js
function generate (line 23) | async function generate(args) {
FILE: tests/acceptance/installTest.js
function install (line 25) | async function install(args) {
FILE: tests/helpers/promise.js
function PromiseExt (line 31) | function PromiseExt(resolver, label) {
FILE: tests/runner.js
function addFiles (line 29) | function addFiles(mocha, files) {
Condensed preview — 86 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (158K chars).
[
{
"path": ".babelrc",
"chars": 84,
"preview": "{\n \"presets\": [\"es2015\", \"stage-1\"],\n\n \"plugins\": [\"transform-regenerator\"]\n}\n"
},
{
"path": ".editorconfig",
"chars": 166,
"preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
},
{
"path": ".eslintignore",
"chars": 76,
"preview": "tests/fixtures/**\ntemplates/**\nlib\n\n# Note: node_modules ignored by default\n"
},
{
"path": ".eslintrc",
"chars": 1294,
"preview": "{\n \"parser\": \"babel-eslint\",\n \"rules\" : {\n \"strict\" : 0,\n \"no-underscore-dangle\" "
},
{
"path": ".gitignore",
"chars": 402,
"preview": "node_modules\nbower_components\n_site\n_posts\n\n#client stuff\n# See client/.gitignore\n\n# Server stuff\n# See server/.gitignor"
},
{
"path": ".npmignore",
"chars": 9,
"preview": "tests\nsrc"
},
{
"path": ".sane-cli",
"chars": 2,
"preview": "{}"
},
{
"path": ".travis.yml",
"chars": 517,
"preview": "language: node_js\nsudo: false\nnode_js:\n - \"0.12\"\n - \"4\"\nbefore_install:\n - npm config set spin false\n - npm install "
},
{
"path": "CHANGELOG.md",
"chars": 7957,
"preview": "# SANE Stack Changelog\n\n### Master\n* [BUGFIX] No longer requires global ember-cli\n\n### 0.1.0-beta.1\n* [FEATURE] Added an"
},
{
"path": "CONTRIBUTING.md",
"chars": 7087,
"preview": "# Getting Involved\n\nOpen source projects like SANE are possible because of people like you who are\nwilling to get involv"
},
{
"path": "LICENSE.md",
"chars": 1126,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Markus Padourek, Artificial Labs and SANE Stack contributors\n\nPermission is he"
},
{
"path": "README.md",
"chars": 4915,
"preview": "<p align=\"center\">\n <img src=\"https://camo.githubusercontent.com/b8ecf54b15f51c7c992d6fce003b661c96d8acec/68747470733a2"
},
{
"path": "appveyor.yml",
"chars": 1564,
"preview": "# http://www.appveyor.com/docs/appveyor-yml\n\n# Leave line endings as-is\ninit:\n - git config --global core.autocrlf inpu"
},
{
"path": "bin/sane",
"chars": 491,
"preview": "#!/usr/bin/env node\n'use strict';\n\n// Provide a title to the process in `ps`\nprocess.title = 'sane';\n\nvar path = require"
},
{
"path": "bin/sane.cmd",
"chars": 63,
"preview": "@SETLOCAL\n@SET PATHEXT=%PATHEXT:;.JS;=;%\nnode \"%~dp0\\sane\" %*\n"
},
{
"path": "ecosystem.json",
"chars": 1269,
"preview": "{\n \"apps\" : [{\n \"name\" : \"SANE App\",\n \"script\" : \"app.js\",\n \"env\": {\n \"COMMON_VARIABLE\": \"true\"\n "
},
{
"path": "package.json",
"chars": 2360,
"preview": "{\n \"name\": \"sane-cli\",\n \"version\": \"0.1.0-beta.1\",\n \"trackingCode\": \"UA-46963158-10\",\n \"description\": \"A command-lin"
},
{
"path": "src/cli.js",
"chars": 5604,
"preview": "'use strict';\n\nvar program = require('commander');\nvar Yam = require"
},
{
"path": "src/commands/generate.js",
"chars": 4039,
"preview": "'use strict';\n\n// with ES6 the same as: var spawn = require('child_process').spawn\nvar { spawn } = require('chil"
},
{
"path": "src/commands/index.js",
"chars": 352,
"preview": "'use strict';\n\n// Load `*.js` under current directory as properties\n// i.e., `User.js` will become `exports['User']` or"
},
{
"path": "src/commands/install.js",
"chars": 1693,
"preview": "'use strict';\n\n// var installNpmPackage = require('../tasks/installNpmPackage');\nvar npmInstall = require('enpeem').i"
},
{
"path": "src/commands/new.js",
"chars": 14307,
"preview": "'use strict';\n\nvar chalk = require('chalk');\nvar checkEnvironment = require('../tasks/checkEnvironment');\nvar"
},
{
"path": "src/commands/sync.js",
"chars": 111,
"preview": "'use strict';\n\nmodule.exports = function generate(/* modelName */) {\n console.log('Not implemented yet.');\n};\n"
},
{
"path": "src/commands/up.js",
"chars": 4559,
"preview": "'use strict';\n\nvar { spawn } = require('child_process');\nvar chalk = require('chalk');\nvar exit "
},
{
"path": "src/helpers/dockerCompose.js",
"chars": 191,
"preview": "'use strict';\n\nvar which = require('which').sync;\n\nmodule.exports = function () {\n try {\n which('docker-compose');\n "
},
{
"path": "src/helpers/ember.js",
"chars": 486,
"preview": "'use strict';\n\nvar ember;\nvar path = require('path');\nvar emberBin = (process.platform === 'win32' ?"
},
{
"path": "src/helpers/npm.js",
"chars": 100,
"preview": "'use strict';\n\nvar npm = (process.platform === 'win32' ? 'npm.cmd' : 'npm');\n\nmodule.exports = npm;\n"
},
{
"path": "src/helpers/promise.js",
"chars": 1732,
"preview": "/* eslint no-proto:0 */\n'use strict';\n\nvar RSVP = require('rsvp');\nvar Promise = RSVP.Promise;\n\nmodule.exports = Prom"
},
{
"path": "src/helpers/sails.js",
"chars": 108,
"preview": "'use strict';\n\nvar sails = (process.platform === 'win32' ? 'sails.cmd' : 'sails');\n\nmodule.exports = sails;\n"
},
{
"path": "src/helpers/setUpTracking.js",
"chars": 696,
"preview": "'use strict';\n\nvar Leek = require('leek');\n\nmodule.exports = function setUpTracking(trackingCode, name, version, config)"
},
{
"path": "src/projectMeta.js",
"chars": 809,
"preview": "'use strict';\n\nvar path = require('path');\nvar which = require('which').sync;\n\n// TODO(markus): Needs some refactoring\n"
},
{
"path": "src/tasks/Project.js",
"chars": 5449,
"preview": "'use strict';\nvar verbose = require('verboser');\nvar assign = require('lodash/object/assign');\nvar pluck = require('lo"
},
{
"path": "src/tasks/checkEnvironment.js",
"chars": 3055,
"preview": "'use strict';\n\n/*\n * Check things like DOCKER_HOST is set, docker/boot2docker/fig (depending on OS), sails and/or ember "
},
{
"path": "src/tasks/copyToProject.js",
"chars": 2982,
"preview": "'use strict';\n\nvar Project = require('./Project');\nvar path = require('path');\nvar fs = require('fs-extra');\nvar jsdiff "
},
{
"path": "src/tasks/generate/createEmberConfig.js",
"chars": 1266,
"preview": "'use strict';\n\nvar path = require('path');\nvar fs = require('fs');\n\n/**\n * @overview This function executes the machin"
},
{
"path": "src/tasks/generate/runMachine.js",
"chars": 5341,
"preview": "var { spawn } = require('child-process-promise');\nvar npmInstall = require('enpeem').install;\nvar fs "
},
{
"path": "src/tasks/getAppNames.js",
"chars": 1690,
"preview": "'use strict';\n\n/*\n * IMPORTANT: Right now only one client and server are supported.\n *\n * Checks the .sane-cli files for"
},
{
"path": "src/tasks/getTemplates.js",
"chars": 924,
"preview": "'use strict';\n\n/*\n * filters out templates in given folderpath, taking only files with given nameBegins\n * and filtering"
},
{
"path": "src/tasks/modelConversion.js",
"chars": 2197,
"preview": "'use strict';\n\n/*\n * Converts ember model attributes to sails model attributes and vice versa\n * Sails supports: string,"
},
{
"path": "src/tasks/renameTemplate.js",
"chars": 348,
"preview": "'use strict';\n\n/**\n * @module sane\n */\n\n/**\n * Removes the .hbs extension from template files.\n *\n * @private\n * @method"
},
{
"path": "src/tasks/trackCommand.js",
"chars": 554,
"preview": "'use strict';\n\n/*\n * Is used to track every typed in command with all the flags to GA.\n */\n\nmodule.exports = function tr"
},
{
"path": "templates/.gitignore",
"chars": 13,
"preview": "node_modules\n"
},
{
"path": "templates/client/app/adapters/application.js",
"chars": 351,
"preview": "import DS from 'ember-data';\n\nexport default DS.RESTAdapter.extend({\n coalesceFindRequests: true,\n namespace: 'api/v1'"
},
{
"path": "templates/client/app/serializers/application.js",
"chars": 72,
"preview": "import DS from 'ember-data';\n\nexport default DS.RESTSerializer.extend();"
},
{
"path": "templates/docker-compose.yml.hbs",
"chars": 951,
"preview": "{{#if database}}\ndb:\n image: {{database}}:latest #postgres, mysql, mongo, redis. You can also specifiy a specific versi"
},
{
"path": "templates/fig.yml.hbs",
"chars": 944,
"preview": "{{#if database}}\ndb:\n image: {{database}}:latest #postgres, mysql, mongo, redis. You can also specifiy a specific versi"
},
{
"path": "templates/package.json.hbs",
"chars": 286,
"preview": "{\n \"name\": \"{{name}}\",\n \"version\": \"0.0.1\",\n \"description\": \"A blank sane stack project.\",\n \"main\": \"index.js\",\n \"s"
},
{
"path": "templates/server/.sailsrc",
"chars": 100,
"preview": "{\n \"hooks\": {\n \"grunt\": false,\n \"views\": false\n },\n \"paths\": {\n \"public\": \"assets\"\n }\n}"
},
{
"path": "templates/server/api/controllers/AppController.js",
"chars": 839,
"preview": "/**\n * AppController\n *\n * @description :: Server-side logic for managing apps\n * @help :: See http://links.sails"
},
{
"path": "templates/server/api/responses/badRequest.js",
"chars": 1071,
"preview": "/**\n * 400 (Bad Request) Handler\n *\n * Usage:\n * return res.badRequest();\n * return res.badRequest(data);\n * return res."
},
{
"path": "templates/server/api/responses/forbidden.js",
"chars": 991,
"preview": "/**\n * 403 (Forbidden) Handler\n *\n * Usage:\n * return res.forbidden();\n * return res.forbidden(err);\n * return res.forbi"
},
{
"path": "templates/server/api/responses/notFound.js",
"chars": 1154,
"preview": "/**\n * 404 (Not Found) Handler\n *\n * Usage:\n * return res.notFound();\n * return res.notFound(err);\n * return res.notFoun"
},
{
"path": "templates/server/api/responses/ok.js",
"chars": 538,
"preview": "/**\n * 200 (OK) Response\n *\n * Usage:\n * return res.ok();\n * return res.ok(data);\n * return res.ok(data, 'auth/login');\n"
},
{
"path": "templates/server/api/responses/serverError.js",
"chars": 1094,
"preview": "/**\n * 500 (Server Error) Response\n *\n * Usage:\n * return res.serverError();\n * return res.serverError(err);\n * return r"
},
{
"path": "templates/server/assets/index.html",
"chars": 191,
"preview": "<h1>Welcome to your backend!<h1><br>\n <h2>When deploying replace this with your great ember app. In the meantime access "
},
{
"path": "templates/server/config/autoreload.js.hbs",
"chars": 554,
"preview": "var path = require('path');\n\nmodule.exports.autoreload = {\n active: process.env.NODE_ENV === undefined || process.env.N"
},
{
"path": "templates/server/config/blueprints.js",
"chars": 9117,
"preview": "/**\n * Blueprint API Configuration\n * (sails.config.blueprints)\n *\n * These settings are for the global configuration of"
},
{
"path": "templates/server/config/connections.js.hbs",
"chars": 5830,
"preview": "/**\n * Connections\n * (sails.config.connections)\n *\n * `Connections` are like \"saved settings\" for your adapters. What'"
},
{
"path": "templates/server/config/models.js.hbs",
"chars": 1523,
"preview": "/**\n * Default model configuration\n * (sails.config.models)\n *\n * Unless you override them, the following properties wil"
},
{
"path": "templates/server/config/routes.js",
"chars": 2895,
"preview": "/**\n * Route Mappings\n * (sails.config.routes)\n *\n * Your routes map URLs to views and controllers.\n *\n * If Sails recei"
},
{
"path": "tests/.eslintrc",
"chars": 37,
"preview": "{\n \"env\": {\n \"mocha\": true\n }\n}\n"
},
{
"path": "tests/acceptance/generateTest.js",
"chars": 4451,
"preview": "'use strict';\n\nvar assertFile = require('../helpers/assertFile');\nvar fs = require('fs-extra');\nvar tmp ="
},
{
"path": "tests/acceptance/helpTest.js",
"chars": 653,
"preview": "/*eslint-env node, mocha, es6 */\n'use strict';\n\nvar { expect } = require('chai');\nvar { execFile } = require('child-proc"
},
{
"path": "tests/acceptance/installTest.js",
"chars": 1737,
"preview": "'use strict';\n\nvar assertFile = require('../helpers/assertFile');\nvar fs = require('fs-extra');\nva"
},
{
"path": "tests/acceptance/newTest.js",
"chars": 6151,
"preview": "/*eslint-env node, mocha, es6 */\n'use strict';\n\nvar fs = require('fs-extra');\nvar path = requi"
},
{
"path": "tests/factories/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "tests/fixtures/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "tests/fixtures/generate/ember/acceptance-test-ember-user-expected.js",
"chars": 119,
"preview": "import DS from 'ember-data';\n\nexport default DS.Model.extend({\n name: DS.attr('string'),\n age: DS.attr('number')\n});\n"
},
{
"path": "tests/fixtures/generate/ember/acceptance-test-empty-user-expected.js",
"chars": 70,
"preview": "import DS from 'ember-data';\n\nexport default DS.Model.extend({\n \n});\n"
},
{
"path": "tests/fixtures/new/acceptance-test-docker-expected.yml",
"chars": 717,
"preview": "db:\n image: postgres:latest #postgres, mysql, mongo, redis. You can also specifiy a specific version instead of using l"
},
{
"path": "tests/fixtures/new/acceptance-test-sane-cli-expected.js",
"chars": 182,
"preview": "{\n \"apps\": [\n \"client\",\n \"server\"\n ],\n \"disableAnalytics\": false,\n \"database\": \"postgresql\",\n \"docker\": false"
},
{
"path": "tests/helpers/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "tests/helpers/acceptanceSetup.js",
"chars": 816,
"preview": "'use strict';\n\n// var { execFile } = require('child-process-promise');\nvar { spawn } = require('child-process-promise'"
},
{
"path": "tests/helpers/assertFile.js",
"chars": 2293,
"preview": "'use strict';\n\nvar { expect } = require('chai');\nvar includes = require('lodash/collection/includes');\nvar flatten "
},
{
"path": "tests/helpers/assertFileEquals.js",
"chars": 699,
"preview": "'use strict';\n\nvar { expect } = require('chai');\nvar fs = require('fs');\n\n/*\n Assert that a given file matches another."
},
{
"path": "tests/helpers/killCliProcess.js",
"chars": 184,
"preview": "'use strict';\n\nmodule.exports = function (childProcess) {\n if (process.platform === 'win32') {\n childProcess.send({k"
},
{
"path": "tests/helpers/promise.js",
"chars": 1732,
"preview": "'use strict';\n\nvar RSVP = require('rsvp');\nvar Promise = RSVP.Promise;\n\nmodule.exports = PromiseExt;\n\n// Utility func"
},
{
"path": "tests/helpers/runCommand.js",
"chars": 2497,
"preview": "'use strict';\n\nvar RSVP = require('rsvp');\nvar chalk = require('chalk');\nvar spawn = require"
},
{
"path": "tests/helpers/sane.js",
"chars": 220,
"preview": "'use strict';\n\nvar path = require('path');\n\n\nvar root = process.cwd();\nvar saneExec = (process.platform === 'win32"
},
{
"path": "tests/runner.js",
"chars": 1165,
"preview": "/*eslint no-process-exit:0*/\n'use strict';\n\nvar glob = require('glob');\nvar Mocha = require('mocha');\nvar chalk = requi"
},
{
"path": "tests/unit/analyticsTest.js",
"chars": 913,
"preview": "// 'use strict';\n// var assert = require('../helpers/assert');\n// var Command = require('../../lib/models/command');\n// "
},
{
"path": "tests/unit/commands/generateTest.js",
"chars": 1813,
"preview": "// 'use strict';\n\n// var GenerateCommand = require('../../../lib/commands/generate');\n// var Promise = require('"
},
{
"path": "tests/unit/commands/newTest.js",
"chars": 1489,
"preview": "/*eslint-env node, mocha, es6 */\n'use strict';\n\nvar { expect } = require('chai');\nvar newCommand = require('../../../lib"
},
{
"path": "tests/unit/getAddonTest.js",
"chars": 2819,
"preview": "'use strict';\n\nvar { expect } = require('chai');\n// var Project = require('../../lib/tasks/Project');\n// var pluck = req"
},
{
"path": "tests/unit/helpers/emberTest.js",
"chars": 419,
"preview": "'use strict';\n\nvar ember = require('../../../lib/helpers/ember');\nvar expect = require('chai').expect;\n\ndescribe('Unit: "
},
{
"path": "tests/unit/tasks/createEmberConfigTest.js",
"chars": 466,
"preview": "'use strict';\n\nvar { expect } = require('chai');\n// var createEmberConfig = require('../../../lib/tasks/generate/"
}
]
About this extraction
This page contains the full source code of the artificialio/sane GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 86 files (144.7 KB), approximately 37.1k tokens, and a symbol index with 18 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.