diff --git a/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-show-test.js b/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-show-test.js index b3a6c02b854..9dca803f172 100644 --- a/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-show-test.js +++ b/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-show-test.js @@ -1,11 +1,11 @@ import Theme from "admin/models/theme"; -import { mapRoutes } from "discourse/mapping-router"; import { moduleFor } from "ember-qunit"; +import { registerRouter } from "discourse/mapping-router"; import { test } from "qunit"; moduleFor("controller:admin-customize-themes-show", { beforeEach() { - this.registry.register("router:main", mapRoutes()); + registerRouter(this.registry); }, needs: ["controller:adminUser"], }); diff --git a/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-test.js b/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-test.js index ada14f06fc9..22c072c766e 100644 --- a/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-test.js +++ b/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-customize-themes-test.js @@ -1,11 +1,11 @@ import Theme from "admin/models/theme"; -import { mapRoutes } from "discourse/mapping-router"; import { moduleFor } from "ember-qunit"; +import { registerRouter } from "discourse/mapping-router"; import { test } from "qunit"; moduleFor("controller:admin-customize-themes", { beforeEach() { - this.registry.register("router:main", mapRoutes()); + registerRouter(this.registry); }, needs: ["controller:adminUser"], }); diff --git a/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-user-badges-test.js b/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-user-badges-test.js index b4ba5a360be..48e2586496d 100644 --- a/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-user-badges-test.js +++ b/app/assets/javascripts/admin/tests/admin/unit/controllers/admin-user-badges-test.js @@ -1,11 +1,11 @@ import Badge from "discourse/models/badge"; -import { mapRoutes } from "discourse/mapping-router"; import { moduleFor } from "ember-qunit"; +import { registerRouter } from "discourse/mapping-router"; import { test } from "qunit"; moduleFor("controller:admin-user-badges", { beforeEach() { - this.registry.register("router:main", mapRoutes()); + registerRouter(this.registry); }, needs: ["controller:adminUser"], }); diff --git a/app/assets/javascripts/discourse-common/addon/resolver.js b/app/assets/javascripts/discourse-common/addon/resolver.js index ac6b668030a..f720febb291 100644 --- a/app/assets/javascripts/discourse-common/addon/resolver.js +++ b/app/assets/javascripts/discourse-common/addon/resolver.js @@ -105,8 +105,9 @@ export function buildResolver(baseName) { dashed = dasherize(suffix), moduleName = Object.keys(requirejs.entries).find(function (e) { return ( - e.indexOf(suffix, e.length - suffix.length) !== -1 || - e.indexOf(dashed, e.length - dashed.length) !== -1 + e.indexOf("/templates/") === -1 && + (e.indexOf(suffix, e.length - suffix.length) !== -1 || + e.indexOf(dashed, e.length - dashed.length) !== -1) ); }); diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.js b/app/assets/javascripts/discourse/app/components/composer-editor.js index 45dc0da37f4..2ed3a8712ac 100644 --- a/app/assets/javascripts/discourse/app/components/composer-editor.js +++ b/app/assets/javascripts/discourse/app/components/composer-editor.js @@ -39,7 +39,6 @@ import { findRawTemplate } from "discourse-common/lib/raw-templates"; import getURL from "discourse-common/lib/get-url"; import { iconHTML } from "discourse-common/lib/icon-library"; import { isTesting } from "discourse-common/config/environment"; - import { loadOneboxes } from "discourse/lib/load-oneboxes"; import putCursorAtEnd from "discourse/lib/put-cursor-at-end"; import userSearch from "discourse/lib/user-search"; diff --git a/app/assets/javascripts/discourse/app/mapping-router.js b/app/assets/javascripts/discourse/app/mapping-router.js index 289c09a9b5f..3f332c6e429 100644 --- a/app/assets/javascripts/discourse/app/mapping-router.js +++ b/app/assets/javascripts/discourse/app/mapping-router.js @@ -141,3 +141,11 @@ export function mapRoutes() { this.route("unknown", { path: "*path" }); }); } + +export function registerRouter(registry) { + registry.unregister("router:main"); + let router = mapRoutes(); + + registry.register("router:main", router); + return router; +} diff --git a/app/assets/javascripts/discourse/app/pre-initializers/discourse-bootstrap.js b/app/assets/javascripts/discourse/app/pre-initializers/discourse-bootstrap.js index 4ecda523b10..be37c27df4e 100644 --- a/app/assets/javascripts/discourse/app/pre-initializers/discourse-bootstrap.js +++ b/app/assets/javascripts/discourse/app/pre-initializers/discourse-bootstrap.js @@ -9,6 +9,7 @@ import I18n from "I18n"; import PreloadStore from "discourse/lib/preload-store"; import RSVP from "rsvp"; import Session from "discourse/models/session"; +import { camelize } from "@ember/string"; import deprecated from "discourse-common/lib/deprecated"; import { setDefaultOwner } from "discourse-common/lib/get-owner"; import { setIconList } from "discourse-common/lib/icon-library"; @@ -26,23 +27,38 @@ export default { if (isTesting()) { return; } - const preloadedDataElement = document.getElementById("data-preloaded"); - const setupData = document.getElementById("data-discourse-setup").dataset; - if (preloadedDataElement) { - const preloaded = JSON.parse(preloadedDataElement.dataset.preloaded); - - Object.keys(preloaded).forEach(function (key) { - PreloadStore.store(key, JSON.parse(preloaded[key])); - - if (setupData.debugPreloadedAppData === "true") { - /* eslint-disable no-console */ - console.log(key, PreloadStore.get(key)); - /* eslint-enable no-console */ - } + let setupData; + let preloaded; + if (app.bootstrap) { + // This is annoying but our old way of using `data-*` attributes used camelCase by default + setupData = {}; + Object.keys(app.bootstrap.setup_data).forEach((k) => { + setupData[camelize(k)] = app.bootstrap.setup_data[k]; }); + preloaded = app.bootstrap.preloaded; } + const setupDataElement = document.getElementById("data-discourse-setup"); + if (setupDataElement) { + setupData = setupDataElement.dataset; + } + + const preloadedDataElement = document.getElementById("data-preloaded"); + if (preloadedDataElement) { + preloaded = JSON.parse(preloadedDataElement.dataset.preloaded); + } + + Object.keys(preloaded).forEach(function (key) { + PreloadStore.store(key, JSON.parse(preloaded[key])); + + if (setupData.debugPreloadedAppData === "true") { + /* eslint-disable no-console */ + console.log(key, PreloadStore.get(key)); + /* eslint-enable no-console */ + } + }); + let baseUrl = setupData.baseUrl; Object.defineProperty(app, "BaseUrl", { get() { @@ -94,8 +110,11 @@ export default { parseInt(setupData.userColorSchemeId, 10) || null; session.userDarkSchemeId = parseInt(setupData.userDarkSchemeId, 10) || -1; - if (isDevelopment()) { - setIconList(JSON.parse(setupData.svgIconList)); + let iconList = setupData.svgIconList; + if (isDevelopment() && iconList) { + setIconList( + typeof iconList === "string" ? JSON.parse(iconList) : iconList + ); } if (setupData.s3BaseUrl) { diff --git a/app/assets/javascripts/discourse/app/pre-initializers/inject-discourse-objects.js b/app/assets/javascripts/discourse/app/pre-initializers/inject-discourse-objects.js index 3e863c33947..a2c605c5452 100644 --- a/app/assets/javascripts/discourse/app/pre-initializers/inject-discourse-objects.js +++ b/app/assets/javascripts/discourse/app/pre-initializers/inject-discourse-objects.js @@ -13,65 +13,53 @@ import User from "discourse/models/user"; const ALL_TARGETS = ["controller", "component", "route", "model", "adapter"]; +export function registerObjects(container, app) { + if (app.__registeredObjects__) { + // don't run registrations twice. + return; + } + app.__registeredObjects__ = true; + + app.register("store:main", Store); + app.register("service:store", Store); + + // backwards compatibility: remove when plugins have updated + app.appEvents = container.lookup("service:app-events"); + + // TODO: This should be included properly + app.register("message-bus:main", MessageBus, { instantiate: false }); + + const siteSettings = app.SiteSettings; + app.register("site-settings:main", siteSettings, { instantiate: false }); +} + export default { name: "inject-discourse-objects", after: "discourse-bootstrap", initialize(container, app) { - ALL_TARGETS.forEach((t) => - app.inject(t, "appEvents", "service:app-events") - ); + registerObjects(container, app); - // backwards compatibility: remove when plugins have updated - app.register("store:main", Store); - app.appEvents = container.lookup("service:app-events"); - - if (!app.hasRegistration("service:store")) { - app.register("service:store", Store); - ALL_TARGETS.forEach((t) => app.inject(t, "store", "service:store")); - } - - app.register("message-bus:main", MessageBus, { instantiate: false }); - - ALL_TARGETS.concat("service").forEach((t) => - app.inject(t, "messageBus", "message-bus:main") - ); - - const siteSettings = app.SiteSettings; - app.register("site-settings:main", siteSettings, { instantiate: false }); - ALL_TARGETS.concat("service").forEach((t) => - app.inject(t, "siteSettings", "site-settings:main") - ); + let siteSettings = container.lookup("site-settings:main"); const currentUser = User.current(); app.register("current-user:main", currentUser, { instantiate: false }); app.currentUser = currentUser; - const topicTrackingState = TopicTrackingState.create({ messageBus: MessageBus, siteSettings, currentUser, }); - app.register("topic-tracking-state:main", topicTrackingState, { - instantiate: false, - }); - ALL_TARGETS.forEach((t) => - app.inject(t, "topicTrackingState", "topic-tracking-state:main") - ); const site = Site.current(); app.register("site:main", site, { instantiate: false }); - ALL_TARGETS.forEach((t) => app.inject(t, "site", "site:main")); - app.register("search-service:main", SearchService); - ALL_TARGETS.forEach((t) => - app.inject(t, "searchService", "search-service:main") - ); + app.register("topic-tracking-state:main", topicTrackingState, { + instantiate: false, + }); const session = Session.current(); app.register("session:main", session, { instantiate: false }); - ALL_TARGETS.forEach((t) => app.inject(t, "session", "session:main")); - app.inject("service", "session", "session:main"); // TODO: Automatically register this service const screenTrack = new ScreenTrack( @@ -82,16 +70,45 @@ export default { ); app.register("service:screen-track", screenTrack, { instantiate: false }); + app.register("location:discourse-location", DiscourseLocation); + + const keyValueStore = new KeyValueStore("discourse_"); + app.register("key-value-store:main", keyValueStore, { instantiate: false }); + app.register("search-service:main", SearchService); + + ALL_TARGETS.forEach((t) => app.inject(t, "store", "service:store")); + + ALL_TARGETS.forEach((t) => + app.inject(t, "appEvents", "service:app-events") + ); + + ALL_TARGETS.concat("service").forEach((t) => + app.inject(t, "messageBus", "message-bus:main") + ); + + ALL_TARGETS.concat("service").forEach((t) => + app.inject(t, "siteSettings", "site-settings:main") + ); + + ALL_TARGETS.forEach((t) => + app.inject(t, "topicTrackingState", "topic-tracking-state:main") + ); + + ALL_TARGETS.forEach((t) => app.inject(t, "site", "site:main")); + + ALL_TARGETS.forEach((t) => + app.inject(t, "searchService", "search-service:main") + ); + + ALL_TARGETS.forEach((t) => app.inject(t, "session", "session:main")); + app.inject("service", "session", "session:main"); + if (currentUser) { ["component", "route", "controller", "service"].forEach((t) => { app.inject(t, "currentUser", "current-user:main"); }); } - app.register("location:discourse-location", DiscourseLocation); - - const keyValueStore = new KeyValueStore("discourse_"); - app.register("key-value-store:main", keyValueStore, { instantiate: false }); ALL_TARGETS.forEach((t) => app.inject(t, "keyValueStore", "key-value-store:main") ); diff --git a/app/assets/javascripts/discourse/app/pre-initializers/map-routes.js b/app/assets/javascripts/discourse/app/pre-initializers/map-routes.js index 7dc0bbf7127..8c4b69dd0f6 100644 --- a/app/assets/javascripts/discourse/app/pre-initializers/map-routes.js +++ b/app/assets/javascripts/discourse/app/pre-initializers/map-routes.js @@ -1,21 +1,27 @@ import Application from "@ember/application"; -import { mapRoutes } from "discourse/mapping-router"; +import Ember from "ember"; +import { registerRouter } from "discourse/mapping-router"; + +let originalBuildInstance; export default { name: "map-routes", after: "inject-discourse-objects", initialize(container, app) { - app.unregister("router:main"); - app.register("router:main", mapRoutes()); + let router = registerRouter(app); + container.registry.register("router:main", router); - // HACK to fix: https://github.com/emberjs/ember.js/issues/10310 - const originalBuildInstance = - originalBuildInstance || Application.prototype.buildInstance; + // TODO: Remove this once we've upgraded Ember everywhere + if (Ember.VERSION.startsWith("3.12")) { + // HACK to fix: https://github.com/emberjs/ember.js/issues/10310 + originalBuildInstance = + originalBuildInstance || Application.prototype.buildInstance; - Application.prototype.buildInstance = function () { - this.buildRegistry(); - return originalBuildInstance.apply(this); - }; + Application.prototype.buildInstance = function () { + this.buildRegistry(); + return originalBuildInstance.apply(this); + }; + } }, }; diff --git a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js index a38e25dac47..7072dbea9ad 100644 --- a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js +++ b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js @@ -203,6 +203,7 @@ export function acceptance(name, optionsOrCallback) { resetSite(currentSettings(), siteChanges); } + getApplication().__registeredObjects__ = false; getApplication().reset(); this.container = getOwner(this); if (loggedIn) { @@ -249,6 +250,7 @@ export function acceptance(name, optionsOrCallback) { initializer.teardown(this.container); } }); + app.__registeredObjects__ = false; app.reset(); // We do this after reset so that the willClearRender will have already fired diff --git a/app/assets/javascripts/discourse/tests/unit/controllers/avatar-selector-test.js b/app/assets/javascripts/discourse/tests/unit/controllers/avatar-selector-test.js index 870d05a78e3..4d701d74c1b 100644 --- a/app/assets/javascripts/discourse/tests/unit/controllers/avatar-selector-test.js +++ b/app/assets/javascripts/discourse/tests/unit/controllers/avatar-selector-test.js @@ -1,11 +1,11 @@ import EmberObject from "@ember/object"; import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; -import { mapRoutes } from "discourse/mapping-router"; +import { registerRouter } from "discourse/mapping-router"; import { test } from "qunit"; discourseModule("Unit | Controller | avatar-selector", function (hooks) { hooks.beforeEach(function () { - this.registry.register("router:main", mapRoutes()); + registerRouter(this.registry); }); test("avatarTemplate", function (assert) {