From 6a6368ce7543a4deb834807df5e6eb31d82812ef Mon Sep 17 00:00:00 2001 From: doktordirk Date: Thu, 24 Mar 2016 12:24:30 +0100 Subject: [PATCH] feat(project): add d.ts file * update to babel 6 * maybe jspm17 ready * add lint-md task * remove unused doc task * renamed index.js to aurelia-orm * make d.ts defs as aurelia does * update dependencies --- .remarkrc | 24 ++++ README.md | 7 ++ build/.eslintrc | 6 + build/babel-options.js | 69 +++++++++-- build/paths.js | 14 ++- build/tasks/build.js | 102 ++++++++++++---- build/tasks/doc.js | 14 --- build/tasks/lint.js | 20 ++- build/tasks/prepare-release.js | 1 - config.js | 115 +++++++++--------- doc/components.md | 10 +- doc/decorators.md | 44 ++++--- doc/entities.md | 3 +- doc/getting-started.md | 18 ++- karma.conf.js | 41 ++++--- package.json | 106 ++++++++++------ src/aurelia-orm.js | 45 +++++++ src/component/association-select.js | 2 +- src/index.js | 29 ----- src/validator/has-association.js | 2 +- test/{index.spec.js => aurelia-orm.js} | 4 +- test/decorator/endpoint.spec.js | 2 +- test/entity-manager.spec.js | 2 +- test/entity.spec.js | 2 +- test/repository.spec.js | 2 +- .../entity/with-custom-repository.js | 2 +- test/resources/entity/with-endpoint.js | 2 +- test/resources/entity/with-name.js | 2 +- test/resources/entity/with-type.js | 2 +- test/validator/has-association.spec.js | 2 +- 30 files changed, 460 insertions(+), 234 deletions(-) create mode 100644 .remarkrc create mode 100644 build/.eslintrc delete mode 100644 build/tasks/doc.js create mode 100644 src/aurelia-orm.js delete mode 100644 src/index.js rename test/{index.spec.js => aurelia-orm.js} (85%) diff --git a/.remarkrc b/.remarkrc new file mode 100644 index 00000000..cf5a741c --- /dev/null +++ b/.remarkrc @@ -0,0 +1,24 @@ +{ + "output": true, + "plugins": { + "lint": { + "maximum-line-length": false, + "heading-style": "atx", + "no-duplicate-headings": false, + "no-undefined-references": false, + "no-shortcut-reference-link": false, + "no-heading-punctuation": ".,;:!", + "list-item-indent": false + } + }, + "settings": { + "gfm": true, + "bullet": "*", + "closeAtx": false, + "fences": true, + "listItemIndent": "1", + "rule": "-", + "ruleRepetition": 10, + "ruleSpaces": false + } +} diff --git a/README.md b/README.md index f14e5fc9..aca4841b 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,17 @@ This library is used directly by applications only. This library can be used in the **browser** only. ## Installation + Installing this module is fairly simple. Run `jspm install github:spoonx/aurelia-orm` from your project root. ## Example + Here's a snippet to give you an idea of what this module does. ### entity/user.js + ```javascript import {Entity, validatedResource} from 'spoonx/aurelia-orm'; import {ensure} from 'aurelia-validation'; @@ -50,6 +53,7 @@ export class UserEntity extends Entity { ``` ### page/some-view-model.js + ```javascript import {EntityManager} from 'spoonx/aurelia-orm'; import {inject} from 'aurelia-framework'; @@ -80,9 +84,11 @@ export class Create { ``` ## Gotchas + When using this module, please keep in mind the following gotchas. ### Bundling + When bundling your aurelia app, the bundler renames your modules (to save space). This is fine, but aurelia-orm allows you to add decorators without values, and uses the module name to set the value. For instance, `@resource()` would use the module's name to set the resource. @@ -91,4 +97,5 @@ So keep in mind: When using aurelia-orm in a bundled application, you must speci For instance, `@decorator('category')`. ## Documentation + You can find usage examples and documentation in the [Getting started](doc/getting-started.md) or the `doc/` directory. diff --git a/build/.eslintrc b/build/.eslintrc new file mode 100644 index 00000000..cc4a83f6 --- /dev/null +++ b/build/.eslintrc @@ -0,0 +1,6 @@ +{ + "rules": { + "no-var": 0, + "no-console": 0 + } +} diff --git a/build/babel-options.js b/build/babel-options.js index 9504abb4..a5b2e4be 100644 --- a/build/babel-options.js +++ b/build/babel-options.js @@ -1,11 +1,60 @@ -module.exports = { - modules: 'system', - moduleIds: false, - comments: false, - compact: false, - stage:2, - optional: [ - "es7.decorators", - "es7.classProperties" - ] +var path = require('path'); +var paths = require('./paths'); + +exports.base = function() { + return { + filename: '', + filenameRelative: '', + sourceMap: true, + sourceRoot: '', + moduleRoot: path.resolve('src').replace(/\\/g, '/'), + moduleIds: false, + comments: false, + compact: false, + code:true, + presets: [ 'es2015-loose', 'stage-1'], + plugins: [ + 'syntax-flow', + 'transform-decorators-legacy', + 'transform-flow-strip-types' + ] + }; +} + +exports['plugin-dts'] = ['babel-dts-generator', { + packageName: paths.packageName, + typings: '', + suppressModulePath: true, + suppressComments: false, + memberOutputFilter: /^_.*/ +}]; + +exports.commonjs = function() { + var options = exports.base(); + options.plugins.push('transform-es2015-modules-commonjs'); + return options; +}; + +exports.amd = function() { + var options = exports.base(); + options.plugins.push('transform-es2015-modules-amd'); + return options; +}; + +exports.system = function() { + var options = exports.base(); + options.plugins.push('transform-es2015-modules-systemjs'); + return options; +}; + +exports.es2015 = function() { + var options = exports.base(); + options.presets = ['stage-1'] + return options; +}; + +exports.dts = function() { + var options = exports.base(); + options.plugins.push(exports['plugin-dts']); + return options; }; diff --git a/build/paths.js b/build/paths.js index 7e993446..667d9855 100644 --- a/build/paths.js +++ b/build/paths.js @@ -1,14 +1,24 @@ var path = require('path'); +var fs = require('fs'); var appRoot = 'src/'; +var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); +// your main file which exports only configure and other modules. +// usually packageName or 'index.js' +var entryFileName = pkg.name + '.js'; module.exports = { root: appRoot, source: appRoot + '**/*.js', + tsSource: [ + appRoot + '**/*.js', // list files to parse for d.ts + '!' + appRoot + entryFileName // exclude entry file + ], html: appRoot + '**/*.html', style: 'styles/**/*.css', output: 'dist/', - doc:'./doc', + doc: './doc', e2eSpecsSrc: 'test/e2e/src/*.js', - e2eSpecsDist: 'test/e2e/dist/' + e2eSpecsDist: 'test/e2e/dist/', + packageName: pkg.name }; diff --git a/build/tasks/build.js b/build/tasks/build.js index 83ecf670..499665b9 100644 --- a/build/tasks/build.js +++ b/build/tasks/build.js @@ -1,57 +1,105 @@ -var gulp = require('gulp'); -var runSequence = require('run-sequence'); -var to5 = require('gulp-babel'); -var paths = require('../paths'); +var gulp = require('gulp'); +var runSequence = require('run-sequence'); +var to5 = require('gulp-babel'); +var paths = require('../paths'); var compilerOptions = require('../babel-options'); -var assign = Object.assign || require('object.assign'); +var assign = Object.assign || require('object.assign'); +var through2 = require('through2'); +var concat = require('gulp-concat'); +var insert = require('gulp-insert'); +var rename = require('gulp-rename'); +var tools = require('aurelia-tools'); +var del = require('del'); +var vinylPaths = require('vinyl-paths'); -gulp.task('build-html-es6', function() { - return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'es6')); -}); +// merged output file name. a folder of paths.packageName is temporarly created in build-dts +var jsName = paths.packageName + '.js'; -gulp.task('build-es6', ['build-html-es6'], function() { - return gulp.src(paths.source) - .pipe(gulp.dest(paths.output + 'es6')); + +gulp.task('build-dts', function() { + var importsToAdd = []; // stores extracted imports + + return gulp.src(paths.tsSource) + //.pipe(tools.sortFiles()) // sort fails with subdirectories! + .pipe(through2.obj(function(file, enc, callback) { // extract all imports to importsToAdd + file.contents = new Buffer(tools.extractImports(file.contents.toString('utf8'), importsToAdd)); + this.push(file); + return callback(); + })) + .pipe(concat(jsName)) // concat all selected files to jsName (now without their imports) + .pipe(insert.transform(function(contents) { // re-add extracted imports on top + return tools.createImportBlock(importsToAdd) + contents; + })) + .pipe(to5(assign({}, compilerOptions.dts()))); // compile to d.ts from file jsName. d.ts file is in folder paths.packageName }); -gulp.task('build-html-commonjs', function() { - return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'commonjs')); +gulp.task('build-es2015', ['build-html-es2015'], function() { + return gulp.src(paths.source) + .pipe(to5(assign({}, compilerOptions.es2015()))) + .pipe(gulp.dest(paths.output + 'es2015')); }); gulp.task('build-commonjs', ['build-html-commonjs'], function() { return gulp.src(paths.source) - .pipe(to5(assign({}, compilerOptions, {modules: 'common'}))) + .pipe(to5(assign({}, compilerOptions.commonjs()))) .pipe(gulp.dest(paths.output + 'commonjs')); }); -gulp.task('build-html-amd', function() { - return gulp.src(paths.html) +gulp.task('build-amd', ['build-html-amd'], function() { + return gulp.src(paths.source) + .pipe(to5(assign({}, compilerOptions.amd()))) .pipe(gulp.dest(paths.output + 'amd')); }); -gulp.task('build-amd', ['build-html-amd'], function() { +gulp.task('build-system', ['build-html-system'], function() { return gulp.src(paths.source) - .pipe(to5(assign({}, compilerOptions, {modules: 'amd'}))) - .pipe(gulp.dest(paths.output + 'amd')); + .pipe(to5(assign({}, compilerOptions.system()))) + .pipe(gulp.dest(paths.output + 'system')); }); -gulp.task('build-html-system', function() { +gulp.task('copy-dts', function() { + var tdsPath = paths.packageName + '/' + paths.packageName + '.d.ts'; + return gulp.src(tdsPath) + .pipe(rename(paths.packageName + '.d.ts')) + .pipe(gulp.dest(paths.output + 'es2015')) + .pipe(gulp.dest(paths.output + 'commonjs')) + .pipe(gulp.dest(paths.output + 'amd')) + .pipe(gulp.dest(paths.output + 'system')); +}); + +gulp.task('remove-dts-folder', function() { + var tdsFolder = paths.packageName; + return gulp.src([tdsFolder]) + .pipe(vinylPaths(del)); +}); + +gulp.task('build-html-es2015', function() { return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'system')); + .pipe(gulp.dest(paths.output + 'es2015')); }); -gulp.task('build-system', ['build-html-system'], function() { - return gulp.src(paths.source) - .pipe(to5(assign({}, compilerOptions, {modules: 'system'}))) +gulp.task('build-html-commonjs', function() { + return gulp.src(paths.html) + .pipe(gulp.dest(paths.output + 'commonjs')); +}); + +gulp.task('build-html-amd', function() { + return gulp.src(paths.html) + .pipe(gulp.dest(paths.output + 'amd')); +}); + +gulp.task('build-html-system', function() { + return gulp.src(paths.html) .pipe(gulp.dest(paths.output + 'system')); }); gulp.task('build', function(callback) { return runSequence( 'clean', - ['build-es6', 'build-commonjs', 'build-amd', 'build-system'], + ['build-es2015', 'build-commonjs', 'build-amd', 'build-system'], + 'build-dts', + 'copy-dts', + 'remove-dts-folder', callback ); }); diff --git a/build/tasks/doc.js b/build/tasks/doc.js deleted file mode 100644 index 642f1910..00000000 --- a/build/tasks/doc.js +++ /dev/null @@ -1,14 +0,0 @@ -var gulp = require('gulp'); -var tools = require('aurelia-tools'); -var paths = require('../paths'); -var yuidoc = require('gulp-yuidoc'); - -gulp.task('doc-generate', function() { - return gulp.src(paths.source) - .pipe(yuidoc.parser(null, 'api.json')) - .pipe(gulp.dest(paths.doc)); -}); - -gulp.task('doc', ['doc-generate'], function() { - tools.transformAPIModel(paths.doc); -}); diff --git a/build/tasks/lint.js b/build/tasks/lint.js index ca0a422a..63d36b61 100644 --- a/build/tasks/lint.js +++ b/build/tasks/lint.js @@ -1,6 +1,12 @@ -var gulp = require('gulp'); -var paths = require('../paths'); +var gulp = require('gulp'); +var paths = require('../paths'); var eslint = require('gulp-eslint'); +var mdlint = require('gulp-remark'); +var squeezeParagraphs = require('remark-squeeze-paragraphs'); +var remarkNormalizeHeadings = require('remark-normalize-headings'); +var remarkValidateLinks = require('remark-validate-links'); +var remarkToc = require('remark-toc'); + gulp.task('lint', function() { return gulp.src(paths.source) @@ -8,3 +14,13 @@ gulp.task('lint', function() { .pipe(eslint.format()) .pipe(eslint.failOnError()); }); + +gulp.task('lint-md', function() { + gulp.src(['*.md', paths.doc + '/*.md', '!' + paths.doc + '/CHANGELOG.md'], {base: './'}) + .pipe(mdlint() + .use(squeezeParagraphs) + .use(remarkNormalizeHeadings) + .use(remarkValidateLinks) + .use(remarkToc, {tight: true, maxDepth: 2}) + ).pipe(gulp.dest('./')); +}); diff --git a/build/tasks/prepare-release.js b/build/tasks/prepare-release.js index 505be936..4c4529b2 100644 --- a/build/tasks/prepare-release.js +++ b/build/tasks/prepare-release.js @@ -29,7 +29,6 @@ gulp.task('prepare-release', function(callback) { 'build', 'lint', 'bump-version', - 'doc', 'changelog', callback ); diff --git a/config.js b/config.js index 92b78372..36f3d9b0 100644 --- a/config.js +++ b/config.js @@ -10,25 +10,24 @@ System.config({ ] }, paths: { - "*": "dist/*", "github:*": "jspm_packages/github/*", "npm:*": "jspm_packages/npm/*" }, map: { - "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.1.2.1", - "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.1.5", - "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.1.6", - "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-beta.1.1.4", - "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-beta.1.0.0", - "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.1.1.2", - "aurelia-validation": "npm:aurelia-validation@0.6.3", - "babel": "npm:babel-core@5.8.35", - "babel-runtime": "npm:babel-runtime@5.8.35", + "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.1.3.0", + "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.2.0", + "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-beta.1.2.0", + "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-beta.1.1.0", + "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.1.2.0", + "aurelia-validation": "npm:aurelia-validation@0.6.6", + "babel": "npm:babel-core@5.8.38", + "babel-runtime": "npm:babel-runtime@5.8.38", "core-js": "npm:core-js@1.2.6", "extend": "npm:extend@3.0.0", "fetch": "github:github/fetch@0.11.0", - "spoonx/aurelia-api": "github:spoonx/aurelia-api@2.0.8", + "spoonx/aurelia-api": "github:spoonx/aurelia-api@2.1.2", "typer": "npm:typer@1.1.0", "github:jspm/nodelibs-assert@0.1.0": { "assert": "npm:assert@1.3.0" @@ -42,59 +41,59 @@ System.config({ "github:jspm/nodelibs-util@0.1.0": { "util": "npm:util@0.10.3" }, - "github:spoonx/aurelia-api@2.0.8": { - "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.1.5", - "aurelia-fetch-client": "npm:aurelia-fetch-client@1.0.0-beta.1.1.1", + "github:spoonx/aurelia-api@2.1.2": { + "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.2.0", + "aurelia-fetch-client": "npm:aurelia-fetch-client@1.0.0-beta.1.2.0", "extend": "npm:extend@3.0.0", "qs": "npm:qs@6.1.0" }, "npm:assert@1.3.0": { "util": "npm:util@0.10.3" }, - "npm:aurelia-binding@1.0.0-beta.1.2.1": { - "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.1.6", - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1", - "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.1.1.1" - }, - "npm:aurelia-dependency-injection@1.0.0-beta.1.1.5": { - "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.1.2", - "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.1.6", - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1" - }, - "npm:aurelia-loader@1.0.0-beta.1.1.1": { - "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.1.6", - "aurelia-path": "npm:aurelia-path@1.0.0-beta.1.1.1" - }, - "npm:aurelia-metadata@1.0.0-beta.1.1.6": { - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1" - }, - "npm:aurelia-pal-browser@1.0.0-beta.1.1.4": { - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1" - }, - "npm:aurelia-polyfills@1.0.0-beta.1.0.0": { - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1" - }, - "npm:aurelia-task-queue@1.0.0-beta.1.1.1": { - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1" - }, - "npm:aurelia-templating@1.0.0-beta.1.1.2": { - "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.1.2.1", - "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.1.5", - "aurelia-loader": "npm:aurelia-loader@1.0.0-beta.1.1.1", - "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.1.2", - "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.1.6", - "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.1.1", - "aurelia-path": "npm:aurelia-path@1.0.0-beta.1.1.1", - "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.1.1.1" - }, - "npm:aurelia-validation@0.6.3": { - "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.1.2.1", - "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.1.5", - "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.1.2", - "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.1.6", - "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.1.1.2" - }, - "npm:babel-runtime@5.8.35": { + "npm:aurelia-binding@1.0.0-beta.1.3.0": { + "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0", + "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.1.2.0" + }, + "npm:aurelia-dependency-injection@1.0.0-beta.1.2.0": { + "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.2.0", + "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0" + }, + "npm:aurelia-loader@1.0.0-beta.1.2.0": { + "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-path": "npm:aurelia-path@1.0.0-beta.1.2.0" + }, + "npm:aurelia-metadata@1.0.0-beta.1.2.0": { + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0" + }, + "npm:aurelia-pal-browser@1.0.0-beta.1.2.0": { + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0" + }, + "npm:aurelia-polyfills@1.0.0-beta.1.1.0": { + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0" + }, + "npm:aurelia-task-queue@1.0.0-beta.1.2.0": { + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0" + }, + "npm:aurelia-templating@1.0.0-beta.1.2.0": { + "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.1.3.0", + "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.2.0", + "aurelia-loader": "npm:aurelia-loader@1.0.0-beta.1.2.0", + "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.2.0", + "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.0", + "aurelia-path": "npm:aurelia-path@1.0.0-beta.1.2.0", + "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.1.2.0" + }, + "npm:aurelia-validation@0.6.6": { + "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.1.3.0", + "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.1.2.0", + "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.2.0", + "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.1.2.0" + }, + "npm:babel-runtime@5.8.38": { "process": "github:jspm/nodelibs-process@0.1.2" }, "npm:core-js@1.2.6": { diff --git a/doc/components.md b/doc/components.md index 0b8f4beb..8a86eb08 100644 --- a/doc/components.md +++ b/doc/components.md @@ -1,7 +1,9 @@ # Components + Aurelia-orm comes bundled with some (at the time of writing just one) components to simplify working with entity data. ## association-select + > The `` component composes a `` would. ### property + This tells the component which property to use from the data sent back by the resource (using the repository). **Defaults to `name`**. ### repository + This tells the component where it can find the data to populate the element. This is a simple `EntityManager.getRepository('resource')`. ### association + Add the association to the criteria, and listen for changes on the association so it can update when it does. -*This attribute accepts arrays, and can be combined with the `manyAssociation` attribute*. +_This attribute accepts arrays, and can be combined with the `manyAssociation` attribute_. This roughly translates to: @@ -68,9 +74,11 @@ repository.find({association: association.id}); ``` ### manyAssociation + Almost exactly the same as the `association` attribute, except for a `many` association. This will look up the data from the association's side. _This attribute does **not** accept arrays, but can be combined with the `association` attribute_. ### criteria + Pass along filter criteria to the element. These will be used to restrict the data returned from the API. diff --git a/doc/decorators.md b/doc/decorators.md index 5941292a..b9e60478 100644 --- a/doc/decorators.md +++ b/doc/decorators.md @@ -1,9 +1,12 @@ # Decorators + Aurelia-orm ships with a couple of decorators that help you configure your entities. ## Small example + Here's an example using (almost) all decorators available: + ```javascript import {Entity, resource, repository, validation, association} from 'spoonx/aurelia-orm'; import {ensure} from 'aurelia-validation'; @@ -27,6 +30,7 @@ export class MyEntity extends Entity { ``` ## @name() + Use this decorator to give your entity a name (fetched using `.getName()` on the entity). This is useful when creating dynamic components that use your entities. ```javascript @@ -40,6 +44,7 @@ class HelloWorld {} ``` ## @resource() + This decorator is probably the most important one. Without it, aurelia-orm won't know what your **custom entity** is all about. The resource maps to the API endpoint it represents. Simply put, resouce `foo` maps to `/foo`. When left empty, the name of the class (.toLowerCase()) will be used as the resource name. This is usually fine. @@ -55,6 +60,7 @@ class HelloWorld {} ``` ## @repository() + Usually, you won't need the `@repository()` decorator. It's used to define a custom repository for your entity (which can be useful if you wish to implement different methods). ```javascript @@ -66,22 +72,27 @@ export class MyEntity extends Entity {} ``` ## @validation() + Use this decorator if you wish to enable validation on your entity. This makes use of [aurelia-validation](https://github.com/aurelia/validation) and exposes a `.getValidation()` method on the entity. ## @validatedResource() + Usually when making a custom entity, it's to add validation. This method simply combines @validation() and @resource() into one simple decorator. It's sugar :) ## @association() + Use this decorator to indicate that a property has a relationship with another entity and thus should be treated as an entity. This decorator has the following effects: -- It will tell aurelia-orm to populate children (nested) upon fetching data from the server. -- It will make sure that calling .asObject() on the entity recursively converts all children to simple objects. -- It will make sure that upon calling .update(), all children get converted to IDs. +* It will tell aurelia-orm to populate children (nested) upon fetching data from the server. +* It will make sure that calling .asObject() on the entity recursively converts all children to simple objects. +* It will make sure that upon calling .update(), all children get converted to IDs. ## @type() + This decorator allows you to add types to your properties. These types will be used **when populating an entity**, to cast the values to given type. This can be useful when, for instance, working with `Date` instances. ### Example + ```javascript import {Entity, type} from 'spoonx/aurelia-orm'; @@ -98,26 +109,29 @@ export class MyEntity extends Entity { ``` ### Accepted types + The accepted types are: -- text -- string -- date -- datetime -- integer -- int -- number -- float -- boolean -- bool -- smart (autodetect based on value) +* text +* string +* date +* datetime +* integer +* int +* number +* float +* boolean +* bool +* smart (autodetect based on value) ## @endpoint() + This decorator allows you to specify which endpoint (see the [aurelia-api documentation](https://github.com/SpoonX/aurelia-api/blob/master/doc/getting-started.md#multiple-endpoints)) to use. When not set, the orm will use the defaultEndpoint. ### Example + An example for a User entity ```javascript @@ -128,6 +142,7 @@ export class User extends Entity {} ``` ### Example + An example for a weather entity ```javascript @@ -138,6 +153,7 @@ export class Weather extends Entity {} ``` ### Bonus: validation + Aurelia-orm extends aurelia-validate, and adds validation for your associations. To add validation for associations, simply use the .hasAssociation() rule like so: diff --git a/doc/entities.md b/doc/entities.md index ab49c98a..067b4efd 100644 --- a/doc/entities.md +++ b/doc/entities.md @@ -1,7 +1,9 @@ # Entities + This document is a collection of snippets and examples considering entities and what they can do. ## Create + Following is a small, but more complete example of how you would implement a create. This example uses [aurelia-validation](https://github.com/aurelia/validation). ### File: entity/user-entity.js @@ -69,5 +71,4 @@ export class Create { working.bind = "requestInFlight"> - ``` diff --git a/doc/getting-started.md b/doc/getting-started.md index fc79bb89..488ef24b 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -1,16 +1,20 @@ # Getting started + In this document, we'll be modifying the skeleton to use aurelia-orm. ## Prerequisites + For this guide, we assume you have the [aurelia skeleton](https://github.com/aurelia/skeleton-navigation) set up. We'll also assume you have [node](https://nodejs.org/en/) and [npm](https://www.npmjs.com/) installed, and that you're familiar with [installing modules](https://docs.npmjs.com/). -Finally, we'll assume that you have [jspm](jspm.io) installed. If you don't, run `npm i -g jspm`. +Finally, we'll assume that you have [jspm](http://jspm.io) installed. If you don't, run `npm i -g jspm`. ## Basic usage + The getting started guide will focus on the easiest use cases imaginable. We'll replace the standard functionality provided by the skeleton with aurelia-orm. ### Installing aurelia-orm + We'll start off by installing aurelia-orm. Head over to your terminal of choice, and navigate to your skeleton. Now run the following command: @@ -19,11 +23,13 @@ Now run the following command: This will install aurelia-orm and [aurelia-api](https://github.com/SpoonX/aurelia-api). Aurelia-orm uses [aurelia-api](https://github.com/SpoonX/aurelia-api) to talk to the server. By default, it will communicate with the domain it's being used on. Seeing how for this guide we'll be doing cross-domain communication, we'll have to configure it, and thus we installed it. ### But first... + Cool, the orm has been installed... But now we want it to _do_ something, right? Head on over to your favorite editor, open up the project and open file `src/users.js`. As you can see, it's using `aurelia-fetch-client` to do the API calls to `https://api.github.com/`. We're going to change that, and make use of aurelia-orm. ### Configuration + This is the boring part. Head back to your editor and open up `src/main.js`. Configure aurelia-api and register `https://api.github.com/` as a new endpoint. You can find more information on this in the [aurelia-api getting started](https://github.com/SpoonX/aurelia-api/blob/master/doc/getting-started.md#multiple-endpoints). @@ -49,6 +55,7 @@ export function configure(aurelia) { All we're doing here, is telling aurelia to register aurelia-api as a plugin, and configuring aurelia-api with a new endpoint. ### Use it + Now head back to `src/users.js`. Change the file to look like this: ```javascript @@ -81,12 +88,14 @@ Here's what we've changed. We've: And done! We've now successfully swapped auth aurelia-fetch-client with `aurelia-orm`. -Head back to your terminal, run `gulp watch` and open the project in your browser. Now, when you navigate to http://localhost:9000/#/users, you'll notice that absolutely nothing has changed; which was the point of this getting started. +Head back to your terminal, run `gulp watch` and open the project in your browser. Now, when you navigate to , you'll notice that absolutely nothing has changed; which was the point of this getting started. ## Customize it + What we've done here, is familiarize ourselves with the way to set this module up. There's a lot more this module can do to make talking to restful api's more comfortable (and organized). ### Registering entities + So far, we've only used a default entity, default repository, and only the .find() method. ```javascript @@ -113,6 +122,7 @@ export function configure(aurelia) { aurelia.start().then(a => a.setRoot()); } ``` + Here's what your `config/entities.js` file might look like: ```javascript @@ -147,6 +157,7 @@ export class Article extends Entity { ``` ### Use it + We can now get cracking. In any ViewModel, you can now get the desired repository, and start querying. Here's an extended example (based on the above code snippets): ```javascript @@ -186,11 +197,10 @@ export class ViewModel { ``` ### Endpoints + Every entity can be configured to use an endpoint (see [decorators](decorators.md#endpoint)). This allows you to use the same entities, the same orm, without worrying about changing the endpoint (api url) to talk to. - - ### Further reading * [Decorators](decorators.md) diff --git a/karma.conf.js b/karma.conf.js index 8fc0e8c5..8bc82e13 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -6,28 +6,15 @@ module.exports = function (config) { // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks : ['jspm', 'jasmine'], - jspm : { - // Edit this to your needs - loadFiles: ['test/setup.js', 'src/**/*.js', 'test/**/*.js'], - paths : { - '*': '*.js' - } + frameworks: ['jspm', 'jasmine'], - }, - 'babelPreprocessor': { - options: { - sourceMap: 'inline', - modules : 'system', - moduleIds: false, - optional : [ - "es7.decorators", - "es7.classProperties" - ] - } + jspm: { + // Edit this to your needs + loadFiles: ['test/setup.js', 'test/**/*.js'], + serveFiles: ['src/**/*.js'], }, - // list of files / patterns to load in the browser. + // list of files / patterns to load in the browser files: [], // list of files to exclude @@ -35,7 +22,21 @@ module.exports = function (config) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: {}, + preprocessors: { + 'test/**/*.js': ['babel'], + 'src/**/*.js': ['babel'] + }, + 'babelPreprocessor': { + options: { + sourceMap: 'inline', + presets: [ 'es2015-loose', 'stage-1'], + plugins: [ + 'syntax-flow', + 'transform-decorators-legacy', + 'transform-flow-strip-types' + ] + } + }, // test results reporter to use // possible values: 'dots', 'progress' diff --git a/package.json b/package.json index 0019eefd..74f4ae9c 100644 --- a/package.json +++ b/package.json @@ -14,72 +14,102 @@ "bugs": { "url": "https://github.com/SpoonX/aurelia-orm/issues" }, - "scripts": { - "test": "./node_modules/.bin/gulp test" - }, "license": "MIT", "author": "RWOverdijk (http://spoonx.nl/)", - "main": "dist/system/index.js", + "main": "dist/commonjs/aurelia-orm.js", "repository": { "type": "git", "url": "https://github.com/SpoonX/aurelia-orm" }, + "scripts": { + "test": "./node_modules/.bin/gulp test" + }, + "jspm": { + "registry": "npm", + "jspmPackage": true, + "main": "aurelia-orm", + "format": "amd", + "directories": { + "dist": "dist/amd" + }, + "dependencies": { + "aurelia-binding": "^1.0.0-beta.1.3.0", + "aurelia-dependency-injection": "^1.0.0-beta.1.2.0", + "aurelia-metadata": "^1.0.0-beta.1.2.0", + "aurelia-templating": "^1.0.0-beta.1.2.0", + "aurelia-validation": "^0.6.6", + "extend": "^3.0.0", + "spoonx/aurelia-api": "github:spoonx/aurelia-api@^2.1.2", + "typer": "^1.1.0" + }, + "peerDependencies": { + "aurelia-binding": "^1.0.0-beta.1.3.0", + "aurelia-dependency-injection": "^1.0.0-beta.1.2.0", + "aurelia-metadata": "^1.0.0-beta.1.2.0", + "aurelia-templating": "^1.0.0-beta.1.2.0", + "aurelia-validation": "^0.6.6", + "extend": "^3.0.0", + "spoonx/aurelia-api": "github:spoonx/aurelia-api@^2.1.2", + "typer": "^1.1.0" + }, + "devDependencies": { + "aurelia-pal-browser": "^1.0.0-beta.1.2.0", + "aurelia-polyfills": "^1.0.0-beta.1.1.0", + "babel": "babel-core@^5.8.38", + "babel-runtime": "^5.8.38", + "core-js": "^1.1.4", + "fetch": "github:github/fetch@^0.11.0" + } + }, + "dependencies": {}, "devDependencies": { - "aurelia-tools": "^0.1.18", + "aurelia-tools": "^0.1.20", + "babel-dts-generator": "^0.4.7", "babel-eslint": "^4.1.3", + "babel-plugin-syntax-flow": "^6.5.0", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-es2015-modules-amd": "^6.6.5", + "babel-plugin-transform-es2015-modules-commonjs": "^6.7.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.6.5", + "babel-plugin-transform-flow-strip-types": "^6.7.0", + "babel-preset-es2015": "^6.6.0", + "babel-preset-es2015-loose": "^7.0.0", + "babel-preset-stage-1": "^6.5.0", "body-parser": "^1.14.2", "conventional-changelog": "0.0.17", "cors": "^2.7.1", "del": "^2.2.0", "express": "^4.13.3", "gulp": "^3.8.10", - "gulp-babel": "^5.1.0", + "gulp-babel": "^6.1.2", "gulp-bump": "^2.0.1", + "gulp-concat": "^2.6.0", "gulp-eslint": "^1.1.1", - "gulp-yuidoc": "^0.1.2", + "gulp-remark": "^1.1.5", + "gulp-insert": "^0.5.0", + "gulp-rename": "^1.2.2", + "gulp-typedoc": "^1.2.1", + "gulp-typedoc-extractor": "0.0.8", "jasmine-core": "^2.1.3", - "jspm": "0.16.15", "karma": "^0.13.15", - "karma-babel-preprocessor": "^5.2.2", + "karma-babel-preprocessor": "^6.0.1", "karma-chrome-launcher": "^0.2.2", "karma-coverage": "^0.5.3", "karma-firefox-launcher": "^0.1.7", "karma-jasmine": "^0.3.6", "karma-jspm": "^2.0.2", "object.assign": "^1.0.3", + "remark": "^4.1.2", + "remark-lint": "^3.0.0", + "remark-normalize-headings": "^0.3.0", + "remark-squeeze-paragraphs": "^3.0.0", + "remark-toc": "^3.0.0", + "remark-validate-links": "^3.0.0", "require-dir": "^0.1.0", "run-sequence": "^1.0.2", "snyk": "^1.8.4", + "through2": "^2.0.0", "vinyl-paths": "^2.1.0", "yargs": "^4.3.2" - }, - "directories": { - "doc": "doc", - "packages": "jspm_packages" - }, - "jspm": { - "main": "index", - "format": "register", - "directories": { - "lib": "dist/system" - }, - "dependencies": { - "aurelia-binding": "npm:aurelia-binding@^1.0.0-beta.1.2.1", - "aurelia-dependency-injection": "npm:aurelia-dependency-injection@^1.0.0-beta.1.1.4", - "aurelia-metadata": "npm:aurelia-metadata@^1.0.0-beta.1.1.5", - "aurelia-templating": "npm:aurelia-templating@^1.0.0-beta.1.1.2", - "aurelia-validation": "npm:aurelia-validation@^0.6.3", - "extend": "npm:extend@^3.0.0", - "spoonx/aurelia-api": "github:spoonx/aurelia-api@^2.0.7", - "typer": "npm:typer@^1.1.0" - }, - "devDependencies": { - "aurelia-pal-browser": "npm:aurelia-pal-browser@^1.0.0-beta.1.1.4", - "aurelia-polyfills": "npm:aurelia-polyfills@^1.0.0-beta.1.0.0", - "babel": "npm:babel-core@^5.8.24", - "babel-runtime": "npm:babel-runtime@^5.8.24", - "core-js": "npm:core-js@^1.1.4", - "fetch": "github:github/fetch@^0.11.0" - } } } diff --git a/src/aurelia-orm.js b/src/aurelia-orm.js new file mode 100644 index 00000000..5fff1fca --- /dev/null +++ b/src/aurelia-orm.js @@ -0,0 +1,45 @@ +import {EntityManager} from './entity-manager'; +import {ValidationGroup} from 'aurelia-validation'; +import {HasAssociationValidationRule} from './validator/has-association'; + +import {DefaultRepository} from './default-repository'; +import {Repository} from './repository'; +import {Entity} from './entity'; +import {OrmMetadata} from './orm-metadata'; +import {association} from './decorator/association'; +import {resource} from './decorator/resource'; +import {endpoint} from './decorator/endpoint'; +import {name} from './decorator/name'; +import {repository} from './decorator/repository'; +import {validation} from './decorator/validation'; +import {type} from './decorator/type'; +import {validatedResource} from './decorator/validated-resource'; + +function configure(aurelia, configCallback) { + let entityManagerInstance = aurelia.container.get(EntityManager); + + configCallback(entityManagerInstance); + + ValidationGroup.prototype.hasAssociation = function() { + return this.isNotEmpty().passesRule(new HasAssociationValidationRule()); + }; + + aurelia.globalResources('./component/association-select'); +} + +export { + configure, + DefaultRepository, + Repository, + Entity, + OrmMetadata, + EntityManager, + association, + resource, + endpoint, + name, + repository, + validation, + type, + validatedResource +}; diff --git a/src/component/association-select.js b/src/component/association-select.js index 213170dc..e1ae5789 100644 --- a/src/component/association-select.js +++ b/src/component/association-select.js @@ -1,7 +1,7 @@ import {inject} from 'aurelia-dependency-injection'; import {bindingMode, BindingEngine} from 'aurelia-binding'; import {bindable, customElement} from 'aurelia-templating'; -import {EntityManager, OrmMetadata, Entity} from '../index'; +import {EntityManager, OrmMetadata, Entity} from '../aurelia-orm'; import extend from 'extend'; @customElement('association-select') diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 28fbf6a3..00000000 --- a/src/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import {EntityManager} from './entity-manager'; -import {ValidationGroup} from 'aurelia-validation'; -import {HasAssociationValidationRule} from './validator/has-association'; - -export {DefaultRepository} from './default-repository'; -export {Repository} from './repository'; -export {Entity} from './entity'; -export {OrmMetadata} from './orm-metadata'; -export {EntityManager} from './entity-manager'; -export {association} from './decorator/association'; -export {resource} from './decorator/resource'; -export {endpoint} from './decorator/endpoint'; -export {name} from './decorator/name'; -export {repository} from './decorator/repository'; -export {validation} from './decorator/validation'; -export {type} from './decorator/type'; -export {validatedResource} from './decorator/validated-resource'; - -export function configure(aurelia, configCallback) { - let entityManagerInstance = aurelia.container.get(EntityManager); - - configCallback(entityManagerInstance); - - ValidationGroup.prototype.hasAssociation = function() { - return this.isNotEmpty().passesRule(new HasAssociationValidationRule()); - }; - - aurelia.globalResources('./component/association-select'); -} diff --git a/src/validator/has-association.js b/src/validator/has-association.js index e05fbbd3..2afd886a 100644 --- a/src/validator/has-association.js +++ b/src/validator/has-association.js @@ -1,5 +1,5 @@ import {ValidationRule} from 'aurelia-validation'; -import {Entity} from '../index'; +import {Entity} from '../aurelia-orm'; export class HasAssociationValidationRule extends ValidationRule { constructor() { diff --git a/test/index.spec.js b/test/aurelia-orm.js similarity index 85% rename from test/index.spec.js rename to test/aurelia-orm.js index 5e001cf2..0d125a77 100644 --- a/test/index.spec.js +++ b/test/aurelia-orm.js @@ -1,9 +1,9 @@ -import {configure} from '../src/index'; +import {configure} from '../src/aurelia-orm'; import {Entity} from '../src/entity-manager'; import {Container} from 'aurelia-dependency-injection'; import {EntityManager} from '../src/entity-manager'; -describe('index', function () { +describe('aurelia-orm', function () { it('Should export a configure method which returns the entityManager', function () { expect(typeof configure).toEqual('function'); diff --git a/test/decorator/endpoint.spec.js b/test/decorator/endpoint.spec.js index 917233fa..1b56cffc 100644 --- a/test/decorator/endpoint.spec.js +++ b/test/decorator/endpoint.spec.js @@ -1,7 +1,7 @@ import {WithEndpoint} from '../resources/entity/with-endpoint'; import {Foo} from '../resources/entity/foo'; import {OrmMetadata} from '../../src/orm-metadata'; -import {EntityManager} from '../../src/index'; +import {EntityManager} from '../../src/aurelia-orm'; import {Container} from 'aurelia-dependency-injection'; import {Config} from 'spoonx/aurelia-api'; diff --git a/test/entity-manager.spec.js b/test/entity-manager.spec.js index 5e836afc..90c48880 100644 --- a/test/entity-manager.spec.js +++ b/test/entity-manager.spec.js @@ -1,4 +1,4 @@ -import {EntityManager} from '../src/index'; +import {EntityManager} from '../src/aurelia-orm'; import {Container} from 'aurelia-dependency-injection'; import {WithResource} from './resources/entity/with-resource'; import {WithCustomRepository} from './resources/entity/with-custom-repository'; diff --git a/test/entity.spec.js b/test/entity.spec.js index 69d23808..f432fcf2 100644 --- a/test/entity.spec.js +++ b/test/entity.spec.js @@ -1,4 +1,4 @@ -import {EntityManager} from '../src/index'; +import {EntityManager} from '../src/aurelia-orm'; import {OrmMetadata, Metadata} from '../src/orm-metadata'; import {WithResource} from './resources/entity/with-resource'; import {WithValidation} from './resources/entity/with-validation'; diff --git a/test/repository.spec.js b/test/repository.spec.js index 7209a5df..0bf9e673 100644 --- a/test/repository.spec.js +++ b/test/repository.spec.js @@ -1,5 +1,5 @@ import {Rest} from 'spoonx/aurelia-api'; -import {EntityManager, Repository, DefaultRepository, Entity} from '../src/index'; +import {EntityManager, Repository, DefaultRepository, Entity} from '../src/aurelia-orm'; import {WithResource} from './resources/entity/with-resource'; import {WithAssociations} from './resources/entity/with-associations'; import {WithCustomRepository} from './resources/entity/with-custom-repository'; diff --git a/test/resources/entity/with-custom-repository.js b/test/resources/entity/with-custom-repository.js index 5bbb3d62..12db692a 100644 --- a/test/resources/entity/with-custom-repository.js +++ b/test/resources/entity/with-custom-repository.js @@ -1,4 +1,4 @@ -import {repository, resource} from '../../../src/index'; +import {repository, resource} from '../../../src/aurelia-orm'; import {Entity} from '../../../src/entity'; import {SimpleCustom} from '../repository/simple-custom'; diff --git a/test/resources/entity/with-endpoint.js b/test/resources/entity/with-endpoint.js index 6b85dcbb..f941ab81 100644 --- a/test/resources/entity/with-endpoint.js +++ b/test/resources/entity/with-endpoint.js @@ -1,5 +1,5 @@ import {Entity} from '../../../src/entity'; -import {resource, endpoint} from '../../../src/index'; +import {resource, endpoint} from '../../../src/aurelia-orm'; @resource() @endpoint('sx/alternative') diff --git a/test/resources/entity/with-name.js b/test/resources/entity/with-name.js index 8a8bb12f..7c5c178c 100644 --- a/test/resources/entity/with-name.js +++ b/test/resources/entity/with-name.js @@ -1,5 +1,5 @@ import {Entity} from '../../../src/entity'; -import {resource, name} from '../../../src/index'; +import {resource, name} from '../../../src/aurelia-orm'; @resource() @name('cool name') diff --git a/test/resources/entity/with-type.js b/test/resources/entity/with-type.js index 532a3f70..b716e116 100644 --- a/test/resources/entity/with-type.js +++ b/test/resources/entity/with-type.js @@ -1,4 +1,4 @@ -import {resource, type} from '../../../src/index'; +import {resource, type} from '../../../src/aurelia-orm'; import {Entity} from '../../../src/entity'; @resource('with-type') diff --git a/test/validator/has-association.spec.js b/test/validator/has-association.spec.js index 89262ee4..e28d1f53 100644 --- a/test/validator/has-association.spec.js +++ b/test/validator/has-association.spec.js @@ -1,7 +1,7 @@ import {HasAssociationValidationRule} from '../../src/validator/has-association'; import {WithAssociationValidation} from '../resources/entity/with-association-validation'; import {Container} from 'aurelia-dependency-injection'; -import {EntityManager, configure} from '../../src/index'; +import {EntityManager, configure} from '../../src/aurelia-orm'; import {ValidationResultProperty} from 'aurelia-validation'; function getEntityManager() {