diff --git a/README.md b/README.md index dd751b33..7ec23b12 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ ember-fetch requries ember-cli 2.13 or above. ```js import Route from '@ember/routing/route'; -import fetch from 'fetch'; +import fetch from 'ember-fetch'; export default Route.extend({ model() { @@ -34,11 +34,22 @@ export default Route.extend({ Available imports: ```js +import fetch, { Headers, Request, Response, AbortController } from 'ember-fetch'; +``` + +`ember-fetch` still supports importing from the old import path, but this will shortly be deprecated and ***will be removed in the next major***: +```js import fetch, { Headers, Request, Response, AbortController } from 'fetch'; ``` ### Use with TypeScript -To use `ember-fetch` with TypeScript or enable editor's type support, You can add `"fetch": ["node_modules/ember-fetch"]` to your `tsconfig.json`. +If you import from `ember-fetch`, the types just work: + +```ts +import fetch from 'ember-fetch'; +``` + +If you're still using the legacy `fetch` import path, you need to add `"fetch": ["node_modules/ember-fetch"]` to your `tsconfig.json`. ```json { @@ -126,7 +137,7 @@ otherwise you can read the status code to determine the bad response type. ```js import Route from '@ember/routing/route'; -import fetch from 'fetch'; +import fetch from 'ember-fetch'; import { isAbortError, isServerErrorResponse, diff --git a/assets/fetch-legacy-alias.js.t b/assets/fetch-legacy-alias.js.t new file mode 100644 index 00000000..7b9b8ccd --- /dev/null +++ b/assets/fetch-legacy-alias.js.t @@ -0,0 +1,13 @@ +/* + * For backwards compatibility, we need to still support consumers importing from the `fetch` path. + * In the future, we should release a breaking change which removes this alias and requires consumers + * to import from `ember-fetch`. This will ensure there is no confusion around folks thinking they are + * importing from the actual `fetch` package. + */ +define('fetch', ['exports', 'ember-fetch'], function(exports, emberFetch) { + const exportKeys = Object.keys(emberFetch); + for (let i = 0; i < exportKeys.length; i++) { + const key = exportKeys[i]; + exports[key] = emberFetch[key]; + } +}); \ No newline at end of file diff --git a/fastboot/instance-initializers/setup-fetch.js b/fastboot/instance-initializers/setup-fetch.js index c41a7b5b..981c5fee 100644 --- a/fastboot/instance-initializers/setup-fetch.js +++ b/fastboot/instance-initializers/setup-fetch.js @@ -1,4 +1,4 @@ -import { setupFastboot } from 'fetch'; +import { setupFastboot } from 'ember-fetch'; /** * To allow relative URLs for Fastboot mode, we need the per request information diff --git a/index.js b/index.js index 00f29664..d52e6f41 100644 --- a/index.js +++ b/index.js @@ -28,12 +28,15 @@ const Rollup = require('broccoli-rollup'); const BroccoliDebug = require('broccoli-debug'); const calculateCacheKeyForTree = require('calculate-cache-key-for-tree'); const VersionChecker = require('ember-cli-version-checker'); +const { readFileSync } = require('fs'); const debug = BroccoliDebug.buildDebugCallback('ember-fetch'); // Path to the template that contains the shim wrapper around the browser polyfill const TEMPLATE_PATH = path.resolve(__dirname + '/assets/browser-fetch.js.t'); +const LEGACY_ALIAS_TEMPLATE = readFileSync(path.resolve(__dirname + '/assets/fetch-legacy-alias.js.t')); + /* * The `index.js` file is the main entry point for all Ember CLI addons. The * object we export from this file is turned into an Addon class @@ -222,7 +225,7 @@ module.exports = { sourceMapConfig: { enabled: false } }), 'after-concat'); - const moduleHeader = this._getModuleHeader(options); + const moduleHeader = LEGACY_ALIAS_TEMPLATE + this._getModuleHeader(options); return debug( new Template(polyfillNode, TEMPLATE_PATH, function (content) { @@ -238,14 +241,14 @@ module.exports = { _getModuleHeader({ hasEmberSourceModules, nativePromise }) { if (hasEmberSourceModules && nativePromise) { return ` -define('fetch', ['exports', 'ember'], function(exports, Ember__module) { +define('ember-fetch', ['exports', 'ember'], function(exports, Ember__module) { 'use strict'; var Ember = 'default' in Ember__module ? Ember__module['default'] : Ember__module;`; } if (hasEmberSourceModules) { return ` -define('fetch', ['exports', 'ember', 'rsvp'], function(exports, Ember__module, RSVP__module) { +define('ember-fetch', ['exports', 'ember', 'rsvp'], function(exports, Ember__module, RSVP__module) { 'use strict'; var Ember = 'default' in Ember__module ? Ember__module['default'] : Ember__module; var RSVP = 'default' in RSVP__module ? RSVP__module['default'] : RSVP__module; @@ -254,13 +257,13 @@ define('fetch', ['exports', 'ember', 'rsvp'], function(exports, Ember__module, R if (nativePromise) { return ` -define('fetch', ['exports'], function(exports) { +define('ember-fetch', ['exports'], function(exports) { 'use strict'; var Ember = originalGlobal.Ember;`; } return ` -define('fetch', ['exports'], function(exports) { +define('ember-fetch', ['exports'], function(exports) { 'use strict'; var Ember = originalGlobal.Ember; var Promise = Ember.RSVP.Promise;`; diff --git a/test/fixtures/dummy/app/routes/index.js b/test/fixtures/dummy/app/routes/index.js index d31658f4..e7ce7846 100644 --- a/test/fixtures/dummy/app/routes/index.js +++ b/test/fixtures/dummy/app/routes/index.js @@ -1,6 +1,6 @@ import Route from '@ember/routing/route'; import { hash } from 'rsvp'; -import fetch, { Request } from 'fetch'; +import fetch, { Request } from 'ember-fetch'; import ajax from 'ember-fetch/ajax'; export default Route.extend({ diff --git a/tests/acceptance/error-test.js b/tests/acceptance/error-test.js index a67924d7..c258ece2 100644 --- a/tests/acceptance/error-test.js +++ b/tests/acceptance/error-test.js @@ -1,6 +1,6 @@ import { module, test } from 'qunit'; import Pretender from 'pretender'; -import fetch, { AbortController } from 'fetch'; +import fetch, { AbortController } from 'ember-fetch'; import { isUnauthorizedResponse, isForbiddenResponse, diff --git a/tests/acceptance/root-test.js b/tests/acceptance/root-test.js index a8ee0365..e542b9e2 100644 --- a/tests/acceptance/root-test.js +++ b/tests/acceptance/root-test.js @@ -2,7 +2,7 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { visit, click, find, currentRouteName } from '@ember/test-helpers'; import Pretender from 'pretender'; -import fetch from 'fetch'; +import fetch from 'ember-fetch'; var server; @@ -32,6 +32,21 @@ module('Acceptance: Root', function(hooks) { assert.equal(this.element.querySelector('.fetch').textContent.trim(), 'Hello World! fetch'); }); + test('legacy import path still works', async function(assert) { + server.get('/omg.json', function() { + return [ + 200, + { 'Content-Type': 'text/json'}, + JSON.stringify({ name: 'World' }) + ]; + }); + + await visit('/legacy-import-path'); + + assert.equal(currentRouteName(), 'legacy-import-path'); + assert.equal(this.element.querySelector('.fetch').textContent.trim(), 'Hello World! fetch'); + }); + test('posting a string', function(assert) { server.post('/upload', function(req) { assert.equal(req.requestBody, 'foo'); diff --git a/tests/dummy/app/controllers/index.js b/tests/dummy/app/controllers/index.js index 357444be..3acbbdae 100644 --- a/tests/dummy/app/controllers/index.js +++ b/tests/dummy/app/controllers/index.js @@ -1,6 +1,6 @@ import Controller from '@ember/controller'; import { run } from '@ember/runloop'; -import fetch from 'fetch'; +import fetch from 'ember-fetch'; export default Controller.extend({ actions: { diff --git a/tests/dummy/app/controllers/legacy-import-path.js b/tests/dummy/app/controllers/legacy-import-path.js new file mode 100644 index 00000000..357444be --- /dev/null +++ b/tests/dummy/app/controllers/legacy-import-path.js @@ -0,0 +1,23 @@ +import Controller from '@ember/controller'; +import { run } from '@ember/runloop'; +import fetch from 'fetch'; + +export default Controller.extend({ + actions: { + fetchSlowData() { + fetch('/slow-data.json') + .then((r) => r.json(), (e) => { + run(() => this.set('fetchedSlowDataFailed', true)); + throw e; + }) + .then((data) => { + run(() => this.setProperties({ fetchedSlowData: data, fetchedSlowDataFailed: false })); + }); + }, + + badFetch() { + fetch('http://localhost:9999') // probably there is nothing listening here :D + .then((r) => r.json(), () => run(() => this.set('fetchFailed', true))); + } + } +}); diff --git a/tests/dummy/app/router.js b/tests/dummy/app/router.js index d0bb0095..33fa8060 100644 --- a/tests/dummy/app/router.js +++ b/tests/dummy/app/router.js @@ -7,6 +7,7 @@ const Router = EmberRouter.extend({ }); Router.map(function() { + this.route('legacy-import-path') }); export default Router; diff --git a/tests/dummy/app/routes/index.js b/tests/dummy/app/routes/index.js index 6f26bf0d..f747b60f 100644 --- a/tests/dummy/app/routes/index.js +++ b/tests/dummy/app/routes/index.js @@ -1,6 +1,6 @@ import Route from '@ember/routing/route'; import { hash } from 'rsvp'; -import fetch from 'fetch'; +import fetch from 'ember-fetch'; export default Route.extend({ model: function() { diff --git a/tests/dummy/app/routes/legacy-import-path.js b/tests/dummy/app/routes/legacy-import-path.js new file mode 100644 index 00000000..6f26bf0d --- /dev/null +++ b/tests/dummy/app/routes/legacy-import-path.js @@ -0,0 +1,13 @@ +import Route from '@ember/routing/route'; +import { hash } from 'rsvp'; +import fetch from 'fetch'; + +export default Route.extend({ + model: function() { + return hash({ + fetch: fetch('/omg.json').then(function(request) { + return request.json(); + }) + }); + } +}); diff --git a/tests/dummy/app/templates/legacy-import-path.hbs b/tests/dummy/app/templates/legacy-import-path.hbs new file mode 100644 index 00000000..70c7c96e --- /dev/null +++ b/tests/dummy/app/templates/legacy-import-path.hbs @@ -0,0 +1,13 @@ +