Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

native classes and decorators #737

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cfb66fc
vendor: upgrade to Ember 3.8.3
bastimeyer Apr 16, 2019
0158a1d
vendor: upgrade to Ember 3.9.1
bastimeyer Apr 16, 2019
9082530
chore: fix volatile comp-prop deprecation
bastimeyer Apr 16, 2019
a9c2e4f
chore: fix clobbering in placeholder comp-prop
bastimeyer Apr 16, 2019
ca3754c
chore: fix clobbering in StreamItemComponent
bastimeyer Apr 16, 2019
e4fc1b8
chore: fix clobbering in SettingsSubmitComponent
bastimeyer Apr 16, 2019
7072c1f
build: update/fix decorators build config
bastimeyer Apr 19, 2019
ef104ae
refactor: fix custom ember-i18n `t` decorator
bastimeyer Apr 19, 2019
91fd548
native classes: form components
bastimeyer Apr 19, 2019
7aec98f
native classes: PolymorphicFragmentSerializer
bastimeyer Apr 23, 2019
077721f
native classes: CustomRESTAdapter
bastimeyer Apr 23, 2019
ad3ea0c
native classes: Settings model and fragments
bastimeyer Apr 23, 2019
2c48a82
native classes: twitch models and serializers
bastimeyer Apr 23, 2019
fdeb860
native classes: github models and serializers
bastimeyer Apr 23, 2019
441ab23
native classes: LS models and adapters
bastimeyer Apr 23, 2019
9e8702a
native classes: stream model
bastimeyer Apr 23, 2019
98d1b82
native classes: services (WIP)
bastimeyer Apr 23, 2019
915f808
native classes: settings route
bastimeyer Apr 26, 2019
e94f1c3
native classes: simple routes
bastimeyer May 1, 2019
c0c5780
native classes: user routes
bastimeyer May 1, 2019
1e6f81a
native classes: channel route
bastimeyer May 1, 2019
8e0e121
data: TwitchUser channel/stream relationships
bastimeyer May 1, 2019
7dd85fe
native classes: helpers
bastimeyer May 13, 2019
968b904
native classes: basic components
bastimeyer May 13, 2019
e95a1b6
native classes: link components
bastimeyer May 14, 2019
448c84b
native classes: basic modal components
bastimeyer May 16, 2019
3212511
native classes: VersioncheckService + related
bastimeyer May 17, 2019
c455843
native classes: AuthService + related
bastimeyer May 23, 2019
e8be729
native classes: ChatService
bastimeyer May 23, 2019
66d2833
native classes: ThemeService
bastimeyer Mar 10, 2020
19236f7
utils: split utils/decorators into sub-modules
bastimeyer Mar 14, 2020
1cbd790
utils: implement computedOverride decorator
bastimeyer Mar 14, 2020
aed58db
native classes: StreamingService
bastimeyer Aug 7, 2020
ba4b0b9
utils: implement hotkey decorators
bastimeyer Aug 7, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion build/tasks/webpack/configurators/ember/decorators.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const webpack = require( "webpack" );
module.exports = function( config, grunt, isProd ) {
config.module.rules.push({
test: /\.js$/,
include: r( pDependencies, "@ember-decorators" ),
include: [
r( pDependencies, "@ember-decorators" ),
r( pDependencies, "ember-decorators-polyfill" )
],
loader: "babel-loader",
options: buildBabelConfig({
plugins: [
Expand Down
20 changes: 18 additions & 2 deletions build/tasks/webpack/configurators/ember/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,29 @@ module.exports = function( config, grunt, isProd ) {
loader: "babel-loader",
options: buildBabelConfig({
plugins: [
[ "@babel/plugin-transform-runtime", {
corejs: false,
helpers: true,
regenerator: false,
useESModules: true,
// https://github.com/babel/babel/issues/9454#issuecomment-460425922
version: "7.2.2"
} ],
// translate @ember imports (requires imports to be ignored in webpack - see below)
"babel-plugin-ember-modules-api-polyfill",
// transform decorators
[ "@babel/plugin-proposal-decorators", {
decoratorsBeforeExport: true
// https://emberjs.github.io/rfcs/0440-decorator-support.html
// https://github.com/babel/ember-cli-babel/blob/v7.7.3/index.js#L341
// Move forward with the Decorators RFC via stage 1 decorators,
// and await a stable future decorators proposal.
legacy: true
} ],
"@babel/plugin-proposal-class-properties"
[ "@babel/plugin-proposal-class-properties", {
// https://babeljs.io/docs/en/babel-plugin-proposal-decorators
// When using the legacy mode, class-properties must be used in loose mode
loose: true
} ]
]
})
});
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"dependencies": {
"bootstrap": "3.3.1",
"ember-source": "3.7.1",
"ember-source": "3.9.1",
"ember-data": "3.9.0",
"ember-data-model-fragments": "4.0.0",
"ember-fetch": "6.5.0",
Expand All @@ -37,14 +37,14 @@
"@babel/plugin-proposal-decorators": "7.3.0",
"@babel/plugin-transform-block-scoping": "7.2.0",
"@babel/plugin-transform-modules-commonjs": "7.2.0",
"@babel/plugin-transform-runtime": "7.4.3",
"@babel/plugin-transform-typescript": "7.4.0",
"@ember/test-helpers": "0.7.27",
"@ember-decorators/argument": "0.8.21",
"@glimmer/syntax": "0.47.4",
"babel-eslint": "10.0.1",
"babel-loader": "8.0.5",
"babel-plugin-debug-macros": "0.2.0",
"babel-plugin-ember-modules-api-polyfill": "2.6.0",
"babel-plugin-ember-modules-api-polyfill": "2.9.0",
"babel-plugin-feature-flags": "0.3.1",
"babel-plugin-filter-imports": "2.0.4",
"babel-plugin-istanbul": "6.0.0",
Expand All @@ -54,7 +54,8 @@
"copy-webpack-plugin": "5.1.1",
"css-loader": "2.1.1",
"ember-compatibility-helpers": "1.0.2",
"ember-decorators": "2.5.1",
"ember-decorators": "6.1.1",
"ember-decorators-polyfill": "1.0.5",
"ember-qunit": "3.5.1",
"eslint": "5.16.0",
"eslint-loader": "3.0.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,118 +1,115 @@
import { get } from "@ember/object";
import Evented from "@ember/object/evented";
import Mixin from "@ember/object/mixin";
import { isNone } from "@ember/utils";
import RESTAdapter from "ember-data/adapters/rest";
import { AdapterError, InvalidError, TimeoutError } from "ember-data/adapters/errors";
import fetch from "fetch";
import { descriptor, urlFragments } from "utils/decorators";


const reURL = /^[a-z]+:\/\/([\w.]+)\/(.+)$/i;
const reURLFragment = /^:(\w+)$/;

const { hasOwnProperty } = {};


@urlFragments({
id( type, id ) {
if ( id === null || id === undefined ) {
throw new Error( "Unknown ID" );
}

return id;
}
})
/**
* Adapter mixin for using static model names
* Adapter for using static model names
* instead of using type.modelName as name
* TODO: Change this and get the URL from the model's adapter instead of the model's toString().
* EmberData has added a more dynamic system for configuring request URLs a long time ago...
*/
export default Mixin.create( Evented, {
mergedProperties: [ "urlFragments" ],
export default class CustomRESTAdapter extends RESTAdapter.extend( Evented ) {
@descriptor({ value: true })
useFetch;

useFetch: true,

urlFragments: {
id( type, id ) {
if ( isNone( id ) ) {
throw new Error( "Unknown ID" );
}

return id;
}
},

findRecord( store, type, id, snapshot ) {
const url = this.buildURL( type, id, snapshot, "findRecord" );

return this.ajax( url, "GET" );
},
}

findAll( store, type, sinceToken ) {
const url = this.buildURL( type, null, null, "findAll" );
const query = sinceToken ? { since: sinceToken } : undefined;

return this.ajax( url, "GET", { data: query } );
},
}

query( store, type, query ) {
const url = this.buildURL( type, null, null, "query", query );
query = this.sortQueryParams ? this.sortQueryParams( query ) : query;

return this.ajax( url, "GET", { data: query } );
},
}

queryRecord( store, type, query ) {
const url = this.buildURL( type, null, null, "queryRecord", query );
query = this.sortQueryParams ? this.sortQueryParams( query ) : query;

return this.ajax( url, "GET", { data: query } );
},
}


createRecordMethod: "POST",
createRecord( store, type, snapshot ) {
createRecordMethod = "POST";
async createRecord( store, type, snapshot ) {
const url = this.buildURL( type, null, snapshot, "createRecord" );
const method = get( this, "createRecordMethod" );
const data = this.createRecordData( store, type, snapshot );

return this.ajax( url, method, data )
.then( data => {
this.trigger( "createRecord", store, type, snapshot );
return data;
});
},
const payload = await this.ajax( url, this.createRecordMethod, data );
this.trigger( "createRecord", store, type, snapshot );

return payload;
}
createRecordData( store, type, snapshot ) {
const data = {};
const serializer = store.serializerFor( type.modelName );
serializer.serializeIntoHash( data, type, snapshot, { includeId: true } );

return { data: data };
},
}

updateRecordMethod: "PUT",
updateRecord( store, type, snapshot ) {
updateRecordMethod = "PUT";
async updateRecord( store, type, snapshot ) {
const url = this.buildURL( type, snapshot.id, snapshot, "updateRecord" );
const method = get( this, "updateRecordMethod" );
const data = this.updateRecordData( store, type, snapshot );

return this.ajax( url, method, data )
.then( data => {
this.trigger( "updateRecord", store, type, snapshot );
return data;
});
},
const payload = await this.ajax( url, this.updateRecordMethod, data );
this.trigger( "updateRecord", store, type, snapshot );

return payload;
}
updateRecordData( store, type, snapshot ) {
const data = {};
const serializer = store.serializerFor( type.modelName );
serializer.serializeIntoHash( data, type, snapshot );

return { data: data };
},
}

deleteRecord( store, type, snapshot ) {
async deleteRecord( store, type, snapshot ) {
const url = this.buildURL( type, snapshot.id, snapshot, "deleteRecord" );

return this.ajax( url, "DELETE" )
.then( data => {
this.trigger( "deleteRecord", store, type, snapshot );
return data;
});
},
const payload = await this.ajax( url, "DELETE" );
this.trigger( "deleteRecord", store, type, snapshot );

return payload;
}


urlForCreateRecord( modelName, snapshot ) {
// Why does Ember-Data do this?
// the id is missing on BuildURLMixin.urlForCreateRecord
return this._buildURL( modelName, snapshot.id );
},
}

/**
* Custom buildURL method with type instead of modelName
Expand All @@ -122,17 +119,16 @@ export default Mixin.create( Evented, {
* @returns {String}
*/
_buildURL( type, id, data ) {
const host = get( this, "host" );
const ns = get( this, "namespace" );
const { host, namespace } = this;
const url = [ host ];

// append the adapter specific namespace
if ( ns ) { url.push( ns ); }
if ( namespace ) { url.push( namespace ); }
// append the type fragments (and process the dynamic ones)
url.push( ...this.buildURLFragments( type, id, data ) );

return url.join( "/" );
},
}

/**
* Dynamic URL fragments
Expand All @@ -142,7 +138,7 @@ export default Mixin.create( Evented, {
* @returns {String[]}
*/
buildURLFragments( type, id, data ) {
const urlFragments = get( this, "urlFragments" );
const urlFragments = this.constructor.urlFragments;
let idFound = false;

const url = String( type )
Expand All @@ -152,7 +148,7 @@ export default Mixin.create( Evented, {
idFound = true;
}

if ( urlFragments.hasOwnProperty( key ) ) {
if ( hasOwnProperty.call( urlFragments, key ) ) {
return urlFragments[ key ].call( this, type, id, data );
}

Expand All @@ -161,33 +157,35 @@ export default Mixin.create( Evented, {
}) );

// append ID if no :id fragment was defined and ID exists
if ( !idFound && !isNone( id ) ) {
if ( !idFound && id !== null && id !== undefined ) {
url.push( id );
}

return url;
},
}


ajax( url ) {
return this._super( ...arguments )
.catch( err => {
if ( err instanceof AdapterError ) {
const _url = reURL.exec( url );
err.host = _url && _url[1] || get( this, "host" );
err.path = _url && _url[2] || get( this, "namespace" );
}
async ajax( url ) {
try {
return await super.ajax( ...arguments );

return Promise.reject( err );
});
},
} catch ( err ) {
if ( err instanceof AdapterError ) {
const _url = reURL.exec( url );
err.host = _url && _url[1] || this.host;
err.path = _url && _url[2] || this.namespace;
}

throw err;
}
}

ajaxOptions() {
const options = this._super( ...arguments );
const options = super.ajaxOptions( ...arguments );
options.cache = "no-cache";

return options;
},
}

_fetchRequest( options ) {
return new Promise( ( resolve, reject ) => {
Expand All @@ -199,12 +197,12 @@ export default Mixin.create( Evented, {
.finally( () => clearTimeout( timeout ) )
.then( resolve, reject );
});
},
}

isSuccess( status, headers, payload ) {
return this._super( ...arguments )
return super.isSuccess( ...arguments )
&& ( payload ? !payload.error : true );
},
}

handleResponse( status, headers, payload ) {
if ( this.isSuccess( status, headers, payload ) ) {
Expand All @@ -220,5 +218,4 @@ export default Mixin.create( Evented, {
status
}]);
}

});
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import JSONSerializer from "ember-data/serializers/json";
const { hasOwnProperty } = {};


export default JSONSerializer.extend({
export default class PolymorphicFragmentSerializer extends JSONSerializer {
/**
* @param {Snapshot} snapshot
* @param {Model} snapshot.record
* @returns {Object}
*/
serialize({ record }) {
const json = this._super( ...arguments );
const json = super.serialize( ...arguments );
const { models, modelBaseName, typeKey } = this;

for ( const [ type, model ] of models ) {
Expand All @@ -22,7 +22,7 @@ export default JSONSerializer.extend({
}

return json;
},
}

/**
* Fix removal of the `typeKey` property
Expand All @@ -32,11 +32,11 @@ export default JSONSerializer.extend({
*/
extractAttributes( modelClass, data ) {
const { typeKey } = this;
const attributes = this._super( ...arguments );
const attributes = super.extractAttributes( ...arguments );
if ( data && hasOwnProperty.call( data, typeKey ) ) {
attributes[ typeKey ] = data[ typeKey ];
}

return attributes;
}
});
}
Loading