DEV: Drop Ember 3 feature flag

This commit is contained in:
David Taylor 2024-02-26 10:53:32 +00:00
parent 2018cd3b62
commit 542cb22fd4
24 changed files with 12 additions and 12136 deletions

View File

@ -1,69 +0,0 @@
# This workflow is designed to stop us accidently committing the lockfile/packagejson symlink changes
# which would cause the default Ember version to change.
name: Ember Version Enforcement
on:
pull_request:
push:
branches:
- main
permissions:
contents: write
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
- name: Yarn install
run: |
cd app/assets/javascripts/discourse
yarn install
- name: "Check default ember version"
working-directory: app/assets/javascripts/discourse
run: |
node -e '
const version = require("ember-source/package.json").version;
console.log("Ember version is", version);
if(version.split(".")[0] !== "5"){
console.log(`Ember has unexpectedly been downgraded to version ${version}. If this is intentional, remove this github workflow.`);
process.exit(1);
}
'
- name: Downgrade ember
run: |
script/switch_ember_version 3
cd app/assets/javascripts/discourse
yarn install
- name: "Check upgraded version"
working-directory: app/assets/javascripts/discourse
run: |
node -e '
const version = require("ember-source/package.json").version;
console.log("Ember version is", version);
if(version.split(".")[0] !== "3"){
console.log(`Expected Ember 3, but found ${version}`);
process.exit(1);
}
'
- name: "Revert ember downgrade"
run: |
script/switch_ember_version 5
- name: "Ensure no diff"
run: |
if [ ! -z "$(git status --porcelain)" ]; then
echo "Working directory was not clean after upgrading/downgrading ember. Perhaps a lockfile is out-of-date. Run this command to re-sync:"
echo " script/regen_ember_3_lockfile"
echo
echo "Current diff:"
echo "---------------------------------------------"
git -c color.ui=always diff
exit 1
fi

View File

@ -1,38 +0,0 @@
# This workflow will run on dependabot pull requests to ensure that they update both the ember3 and ember5 lockfiles
name: Ember Version Lockfiles
on:
- pull_request
permissions:
contents: write
jobs:
help_dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Move lockfile to correct location
run: |
# Dependabot gets confused by the symlinks and dumps the updated lockfile in the root of the repo.
# Let's move it back to the correct location.
if [[ -f yarn-ember5.lock ]]; then
mv yarn-ember5.lock app/assets/javascripts/yarn-ember5.lock
fi
- name: Update ember3 lockfile
run: script/regen_ember_3_lockfile
- name: Push changes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ ! -z "$(git status --porcelain)" ]; then
git config --global user.email "build@discourse.org"
git config --global user.name "discoursebuild"
git add .
git commit -m "Update lockfiles for ember version flag"
git push
fi

View File

@ -22,7 +22,7 @@ permissions:
jobs:
build:
if: github.event_name == 'pull_request' || github.repository != 'discourse/discourse-private-mirror'
name: ${{ matrix.target }} ${{ matrix.build_type }}${{ !matrix.updated_ember && ' (Ember 3)' || '' }} # Update fetch-job-id step if changing this
name: ${{ matrix.target }} ${{ matrix.build_type }} # Update fetch-job-id step if changing this
runs-on: ${{ (matrix.build_type == 'annotations') && 'ubuntu-latest' || 'ubuntu-20.04-8core' }}
container: discourse/discourse_test:slim${{ (matrix.build_type == 'frontend' || matrix.build_type == 'system') && '-browsers' || '' }}${{ (matrix.ruby == '3.1') && '-ruby-3.1.0' || '' }}
timeout-minutes: 20
@ -35,7 +35,6 @@ jobs:
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }}
CAPYBARA_DEFAULT_MAX_WAIT_TIME: 10
MINIO_RUNNER_LOG_LEVEL: DEBUG
EMBER_VERSION: ${{ (matrix.updated_ember && '5') || '3' }}
DISCOURSE_TURBO_RSPEC_RETRY_AND_LOG_FLAKY_TESTS: ${{ (matrix.build_type == 'system' || matrix.build_type == 'backend') && github.ref == 'refs/main/head' }}
strategy:
@ -45,7 +44,6 @@ jobs:
build_type: [backend, frontend, system, annotations]
target: [core, plugins, themes]
ruby: ["3.2"]
updated_ember: [true]
exclude:
- build_type: annotations
target: plugins
@ -58,7 +56,6 @@ jobs:
include:
- build_type: system
target: chat
updated_ember: true
steps:
- name: Set working directory owner
@ -299,7 +296,7 @@ jobs:
id: fetch-job-id
if: always() && steps.check-flaky-spec-report.outputs.exists == 'true'
run: |
job_id=$(ruby script/get_github_workflow_run_job_id.rb ${{ github.run_id }} ${{ github.run_attempt }} '${{ matrix.target }} ${{ matrix.build_type }}${{ !matrix.updated_ember && ' (Ember 3)' || '' }}')
job_id=$(ruby script/get_github_workflow_run_job_id.rb ${{ github.run_id }} ${{ github.run_attempt }} '${{ matrix.target }} ${{ matrix.build_type }}')
echo "job_id=$job_id" >> $GITHUB_OUTPUT
- name: Create flaky tests report artifact
@ -332,7 +329,7 @@ jobs:
core_frontend_tests:
if: github.event_name == 'pull_request' || github.repository != 'discourse/discourse-private-mirror'
name: core frontend (${{ matrix.browser }})${{ !matrix.updated_ember && ' (Ember 3)' || '' }}
name: core frontend (${{ matrix.browser }})
runs-on: ubuntu-20.04-8core
container:
image: discourse/discourse_test:slim-browsers
@ -344,10 +341,6 @@ jobs:
fail-fast: false
matrix:
browser: ["Chrome", "Firefox ESR", "Firefox Evergreen"]
updated_ember: [true]
include:
- browser: Chrome
updated_ember: false
env:
TESTEM_BROWSER: ${{ (startsWith(matrix.browser, 'Firefox') && 'Firefox') || matrix.browser }}
@ -374,10 +367,6 @@ jobs:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-cachev2
- name: Downgrade Ember
if: matrix.updated_ember == false
run: script/switch_ember_version 3
- name: Yarn install
working-directory: ./app/assets/javascripts/discourse
run: yarn install --frozen-lockfile

View File

@ -1,12 +0,0 @@
{{! Remove when legacy modals are dropped (deprecation: discourse.modal-controllers) }}
<div
id={{@id}}
class={{concat-class "modal-body" @class}}
tabindex="-1"
{{did-insert this.didInsert}}
{{will-destroy this.willDestroy}}
...attributes
>
{{yield}}
</div>

View File

@ -1,72 +0,0 @@
// Remove when legacy modals are dropped (deprecation: discourse.modal-controllers)
import Component from "@glimmer/component";
import { DEBUG } from "@glimmer/env";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import $ from "jquery";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
const LEGACY_ERROR =
"d-modal-body should only be used inside a legacy controller-based d-modal. https://meta.discourse.org/t/268057";
function pick(object, keys) {
const result = {};
for (const key of keys) {
if (key in object) {
result[key] = object[key];
}
}
return result;
}
@disableImplicitInjections
export default class DModalBody extends Component {
@service appEvents;
@service modal;
@tracked fixed = false;
@action
didInsert(element) {
if (element.closest(".d-modal:not(.d-modal-legacy)")) {
// eslint-disable-next-line no-console
console.error(LEGACY_ERROR);
if (DEBUG) {
throw new Error(LEGACY_ERROR);
}
}
this.appEvents.trigger("modal-body:clearFlash");
const fixedParent = element.closest(".d-modal.fixed-modal");
if (fixedParent) {
this.fixed = true;
$(fixedParent).modal("show");
this.modal.hidden = false;
}
this.appEvents.trigger(
"modal:body-shown",
pick(this.args, [
"title",
"rawTitle",
"fixed",
"subtitle",
"rawSubtitle",
"submitOnEnter",
"dismissable",
"headerClass",
"modalClass",
"titleAriaElementId",
])
);
}
@action
willDestroy() {
super.willDestroy(...arguments);
this.appEvents.trigger("modal:body-dismissed");
}
}

View File

@ -1,95 +0,0 @@
{{! Remove when legacy modals are dropped (deprecation: discourse.modal-controllers) }}
{{! template-lint-disable no-pointer-down-event-binding }}
{{! template-lint-disable no-invalid-interactive }}
<div
class={{concat-class
this.modalClass
this.modalStyle
(if this.hasPanels "has-panels")
(if @hidden "hidden")
"d-modal-legacy"
}}
id={{if (not-eq this.modalStyle "inline-modal") "discourse-modal"}}
data-keyboard="false"
aria-modal="true"
role="dialog"
aria-labelledby={{this.ariaLabelledby}}
...attributes
{{did-insert this.setupListeners}}
{{will-destroy this.cleanupListeners}}
{{on "mousedown" this.handleMouseDown}}
>
<div class="modal-outer-container">
<div class="modal-middle-container">
<div class="modal-inner-container">
<PluginOutlet @name="above-modal-header" @connectorTagName="div" />
<div class="modal-header {{this.headerClass}}">
{{#if this.dismissable}}
<DButton
@icon="times"
@action={{route-action "closeModal" "initiatedByCloseButton"}}
@title="modal.close"
class="btn-flat modal-close close"
/>
{{/if}}
<div class="modal-title-wrapper">
{{#if this.title}}
<div class="title">
<h3 id="discourse-modal-title">{{this.title}}</h3>
{{#if this.subtitle}}
<p class="subtitle">{{this.subtitle}}</p>
{{/if}}
</div>
{{/if}}
<span id="modal-header-after-title"></span>
</div>
{{#if this.panels}}
<ul class="modal-tabs">
{{#each this.panels as |panel|}}
<ModalTab
@panel={{panel}}
@panelsLength={{this.panels.length}}
@selectedPanel={{@selectedPanel}}
@onSelectPanel={{@onSelectPanel}}
/>
{{/each}}
</ul>
{{/if}}
</div>
<div
id="modal-alert"
role="alert"
class={{if
this.flash
(concat-class
"alert" (concat "alert-" (or this.flash.messageClass "success"))
)
}}
>
{{~this.flash.text~}}
</div>
{{outlet "modalBody"}}
{{#each this.errors as |error|}}
<div class="alert alert-error">
<button
type="button"
class="close"
data-dismiss="alert"
aria-label={{i18n "modal.dismiss_error"}}
>×</button>
{{error}}
</div>
{{/each}}
</div>
</div>
</div>
</div>

View File

@ -1,253 +0,0 @@
// Remove when legacy modals are dropped (deprecation: discourse.modal-controllers)
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { next, schedule } from "@ember/runloop";
import { inject as service } from "@ember/service";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
import { bind } from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
@disableImplicitInjections
export default class DModal extends Component {
@service appEvents;
@service modal;
@tracked wrapperElement;
@tracked modalBodyData = {};
@tracked flash;
get modalStyle() {
if (this.args.modalStyle === "inline-modal") {
return "inline-modal";
} else {
return "fixed-modal";
}
}
get submitOnEnter() {
if ("submitOnEnter" in this.modalBodyData) {
return this.modalBodyData.submitOnEnter;
} else {
return true;
}
}
get dismissable() {
if ("dismissable" in this.modalBodyData) {
return this.modalBodyData.dismissable;
} else {
return true;
}
}
get title() {
if (this.modalBodyData.title) {
return I18n.t(this.modalBodyData.title);
} else if (this.modalBodyData.rawTitle) {
return this.modalBodyData.rawTitle;
} else {
return this.args.title;
}
}
get subtitle() {
if (this.modalBodyData.subtitle) {
return I18n.t(this.modalBodyData.subtitle);
}
return this.modalBodyData.rawSubtitle || this.args.subtitle;
}
get headerClass() {
return this.modalBodyData.headerClass;
}
get panels() {
return this.args.panels;
}
get errors() {
return this.args.errors;
}
@action
setupListeners(element) {
this.appEvents.on("modal:body-shown", this._modalBodyShown);
this.appEvents.on("modal-body:flash", this._flash);
this.appEvents.on("modal-body:clearFlash", this._clearFlash);
document.documentElement.addEventListener(
"keydown",
this._handleModalEvents
);
this.wrapperElement = element;
}
@action
cleanupListeners() {
this.appEvents.off("modal:body-shown", this._modalBodyShown);
this.appEvents.off("modal-body:flash", this._flash);
this.appEvents.off("modal-body:clearFlash", this._clearFlash);
document.documentElement.removeEventListener(
"keydown",
this._handleModalEvents
);
}
get ariaLabelledby() {
if (this.modalBodyData.titleAriaElementId) {
return this.modalBodyData.titleAriaElementId;
} else if (this.args.titleAriaElementId) {
return this.args.titleAriaElementId;
} else if (this.args.title) {
return "discourse-modal-title";
}
}
get modalClass() {
return this.modalBodyData.modalClass || this.args.modalClass;
}
triggerClickOnEnter(e) {
if (!this.submitOnEnter) {
return false;
}
// skip when in a form or a textarea element
if (
e.target.closest("form") ||
(document.activeElement && document.activeElement.nodeName === "TEXTAREA")
) {
return false;
}
return true;
}
@action
handleMouseDown(e) {
if (!this.dismissable) {
return;
}
if (
e.target.classList.contains("modal-middle-container") ||
e.target.classList.contains("modal-outer-container")
) {
// Send modal close (which bubbles to ApplicationRoute) if clicked outside.
// We do this because some CSS of ours seems to cover the backdrop and makes
// it unclickable.
return this.args.closeModal?.("initiatedByClickOut");
}
}
@bind
_modalBodyShown(data) {
if (this.isDestroying || this.isDestroyed) {
return;
}
if (data.fixed) {
this.modal.hidden = false;
}
this.modalBodyData = data;
next(() => {
schedule("afterRender", () => {
this._trapTab();
});
});
}
@bind
_handleModalEvents(event) {
if (this.args.hidden) {
return;
}
if (event.key === "Escape" && this.dismissable) {
next(() => this.args.closeModal("initiatedByESC"));
}
if (event.key === "Enter" && this.triggerClickOnEnter(event)) {
this.wrapperElement.querySelector(".modal-footer .btn-primary")?.click();
event.preventDefault();
}
if (event.key === "Tab") {
this._trapTab(event);
}
}
_trapTab(event) {
if (this.args.hidden) {
return true;
}
const innerContainer = this.wrapperElement.querySelector(
".modal-inner-container"
);
if (!innerContainer) {
return;
}
let focusableElements =
'[autofocus], a, input, select, textarea, summary, [tabindex]:not([tabindex="-1"])';
if (!event) {
// on first trap we don't allow to focus modal-close
// and apply manual focus only if we don't have any autofocus element
const autofocusedElement = innerContainer.querySelector("[autofocus]");
if (
!autofocusedElement ||
document.activeElement !== autofocusedElement
) {
// if there's not autofocus, or the activeElement, is not the autofocusable element
// attempt to focus the first of the focusable elements or just the modal-body
// to make it possible to scroll with arrow down/up
(
autofocusedElement ||
innerContainer.querySelector(
focusableElements + ", button:not(.modal-close)"
) ||
innerContainer.querySelector(".modal-body")
)?.focus();
}
return;
}
focusableElements += ", button:enabled";
const firstFocusableElement =
innerContainer.querySelector(focusableElements);
const focusableContent = innerContainer.querySelectorAll(focusableElements);
const lastFocusableElement = focusableContent[focusableContent.length - 1];
if (event.shiftKey) {
if (document.activeElement === firstFocusableElement) {
lastFocusableElement?.focus();
event.preventDefault();
}
} else {
if (document.activeElement === lastFocusableElement) {
(
innerContainer.querySelector(".modal-close") || firstFocusableElement
)?.focus();
event.preventDefault();
}
}
}
@bind
_clearFlash() {
this.flash = null;
}
@bind
_flash(msg) {
this.flash = msg;
}
}

View File

@ -9,26 +9,4 @@
@closeModal={{this.closeModal}}
/>
{{/each}}
{{/if}}
{{#if this.renderLegacy}}
<DModalLegacy
@modalClass={{if
this.modal.isLegacy
(concat-class
"modal"
"d-modal"
this.modal.modalClass
(if this.modal.opts.panels "has-tabs")
)
}}
@title={{this.modal.title}}
@titleAriaElementId={{this.modal.opts.titleAriaElementId}}
@panels={{this.modal.opts.panels}}
@selectedPanel={{this.modal.selectedPanel}}
@onSelectPanel={{this.modal.onSelectPanel}}
@hidden={{this.modal.hidden}}
@errors={{this.modal.errors}}
@closeModal={{this.closeModal}}
/>
{{/if}}

View File

@ -1,7 +1,6 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { EMBER_MAJOR_VERSION } from "discourse/lib/ember-version";
export default class ModalContainer extends Component {
@service modal;
@ -10,8 +9,4 @@ export default class ModalContainer extends Component {
closeModal(data) {
this.modal.close(data);
}
get renderLegacy() {
return EMBER_MAJOR_VERSION < 4;
}
}

View File

@ -3,7 +3,6 @@ import Component from "@ember/component";
import EmberObject from "@ember/object";
import Ember from "ember";
import { actionModifier } from "./ember-action-modifier";
import { EMBER_MAJOR_VERSION } from "./ember-version";
/**
* Classic Ember components (i.e. "@ember/component") rely upon "event
@ -127,16 +126,13 @@ function rewireClassicComponentEvents(app) {
allEventMethods[methodName] = event;
}
const triggerOverrideMethod =
EMBER_MAJOR_VERSION < 4 ? "trigger" : "_trigger";
// Avoid Component.reopen to stop `ember.component.reopen` deprecation warning
EmberObject.reopen.call(Component, {
/**
* @param {string | typeof INTERNAL} name
* @param {unknown[]} args
*/
[triggerOverrideMethod](name, ...args) {
_trigger(name, ...args) {
if (name === INTERNAL) {
if (this.element) {
return this._super.call(this, ...args);

View File

@ -1,16 +1,9 @@
import { tracked } from "@glimmer/tracking";
import { getOwner } from "@ember/application";
import { action } from "@ember/object";
import Service, { inject as service } from "@ember/service";
import { dasherize } from "@ember/string";
import $ from "jquery";
import { CLOSE_INITIATED_BY_MODAL_SHOW } from "discourse/components/d-modal";
import { EMBER_MAJOR_VERSION } from "discourse/lib/ember-version";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
import deprecated, {
withSilencedDeprecations,
} from "discourse-common/lib/deprecated";
import I18n from "discourse-i18n";
import deprecated from "discourse-common/lib/deprecated";
const LEGACY_OPTS = new Set([
"admin",
@ -23,7 +16,7 @@ const LEGACY_OPTS = new Set([
]);
@disableImplicitInjections
class ModalService extends Service {
export default class ModalService extends Service {
@service dialog;
@tracked activeModal;
@ -93,195 +86,3 @@ class ModalService extends Service {
this.opts = {};
}
}
// Remove all logic below when legacy modals are dropped (deprecation: discourse.modal-controllers)
class ModalServiceWithLegacySupport extends ModalService {
@service appEvents;
@tracked name;
@tracked selectedPanel;
@tracked hidden = true;
@tracked titleOverride;
@tracked modalClassOverride;
@tracked onSelectPanel;
get title() {
if (this.titleOverride) {
return this.titleOverride;
} else if (this.opts.titleTranslated) {
return this.opts.titleTranslated;
} else if (this.opts.title) {
return I18n.t(this.opts.title);
} else {
return null;
}
}
set title(value) {
this.titleOverride = value;
}
get modalClass() {
if (!this.isLegacy) {
return null;
}
return (
this.modalClassOverride ||
this.opts.modalClass ||
`${dasherize(this.name.replace(/^modals\//, "")).toLowerCase()}-modal`
);
}
set modalClass(value) {
this.modalClassOverride = value;
}
show(modal, opts = {}) {
if (typeof modal !== "string") {
return super.show(modal, opts);
}
this.close({ initiatedBy: CLOSE_INITIATED_BY_MODAL_SHOW });
deprecated(
`Defining modals using a controller is deprecated. Use the component-based API instead. (modal: ${modal})`,
{
id: "discourse.modal-controllers",
since: "3.1",
dropFrom: "3.2",
url: "https://meta.discourse.org/t/268057",
}
);
const name = modal;
const container = getOwner(this);
const route = container.lookup("route:application");
this.opts = opts;
const controllerName = opts.admin ? `modals/${name}` : name;
this.name = controllerName;
let controller = container.lookup("controller:" + controllerName);
const templateName = opts.templateName || dasherize(name);
const renderArgs = { into: "application", outlet: "modalBody" };
if (controller) {
renderArgs.controller = controllerName;
} else {
// use a basic controller
renderArgs.controller = "basic-modal-body";
controller = container.lookup(`controller:${renderArgs.controller}`);
}
if (opts.addModalBodyView) {
renderArgs.view = "modal-body";
}
const modalName = `modal/${templateName}`;
const fullName = opts.admin ? `admin/templates/${modalName}` : modalName;
// Any use of the legacy modal system will trigger Discourse's own deprecation message
// so we can silence Ember's message here.
withSilencedDeprecations("route-render-template", () => {
route.render(fullName, renderArgs);
});
if (opts.panels) {
if (controller.actions.onSelectPanel) {
this.onSelectPanel = controller.actions.onSelectPanel.bind(controller);
}
this.selectedPanel = opts.panels[0];
}
controller.set("modal", this);
const model = opts.model;
if (model) {
controller.set("model", model);
}
if (controller.onShow) {
controller.onShow();
}
controller.set("flashMessage", null);
return (this.activeController = controller);
}
close(initiatedBy) {
if (!this.isLegacy) {
super.close(...arguments);
}
const controllerName = this.name;
const controller = controllerName
? getOwner(this).lookup(`controller:${controllerName}`)
: null;
if (controller?.beforeClose?.() === false) {
return;
}
const applicationRoute = getOwner(this).lookup("route:application");
// Any use of the legacy modal system will trigger Discourse's own deprecation message
// so we can silence Ember's message here.
withSilencedDeprecations("route-render-template", () => {
applicationRoute.render("hide-modal", {
into: "application",
outlet: "modalBody",
});
});
$(".d-modal.fixed-modal").modal("hide");
if (controller) {
this.appEvents.trigger("modal:closed", {
name: controllerName,
controller,
});
if (controller.onClose) {
controller.onClose({
initiatedByCloseButton: initiatedBy === "initiatedByCloseButton",
initiatedByClickOut: initiatedBy === "initiatedByClickOut",
initiatedByESC: initiatedBy === "initiatedByESC",
});
}
}
this.hidden = true;
this.name =
this.selectedPanel =
this.modalClassOverride =
this.titleOverride =
this.onSelectPanel =
null;
super.close();
}
hide() {
if (this.isLegacy) {
$(".d-modal.fixed-modal").modal("hide");
} else {
throw "hide/reopen are not supported for component-based modals";
}
}
reopen() {
if (this.isLegacy) {
$(".d-modal.fixed-modal").modal("show");
} else {
throw "hide/reopen are not supported for component-based modals";
}
}
get isLegacy() {
return this.name && !this.activeModal;
}
}
export default EMBER_MAJOR_VERSION >= 4
? ModalService
: ModalServiceWithLegacySupport;

View File

@ -1,11 +1,6 @@
const EMBER_MAJOR_VERSION = parseInt(
require("ember-source/package.json").version.split(".")[0],
10
);
module.exports = {
"application-template-wrapper": false,
"default-async-observers": true,
"jquery-integration": EMBER_MAJOR_VERSION < 4,
"jquery-integration": false,
"template-only-glimmer-components": true,
};

View File

@ -18,25 +18,8 @@ const withSideWatch = require("./lib/with-side-watch");
const RawHandlebarsCompiler = require("discourse-hbr/raw-handlebars-compiler");
const crypto = require("crypto");
const EMBER_MAJOR_VERSION = parseInt(
require("ember-source/package.json").version.split(".")[0],
10
);
process.env.BROCCOLI_ENABLED_MEMOIZE = true;
function filterForEmberVersion(tree) {
if (EMBER_MAJOR_VERSION < 4) {
return tree;
}
return funnel(tree, {
// d-modal-legacy includes a named outlet which would cause
// a build failure in modern Ember
exclude: ["**/components/d-modal-legacy.hbs"],
});
}
module.exports = function (defaults) {
const discourseRoot = path.resolve("../../../..");
const vendorJs = discourseRoot + "/vendor/assets/javascripts/";
@ -92,21 +75,12 @@ module.exports = function (defaults) {
},
trees: {
app: filterForEmberVersion(
RawHandlebarsCompiler(
withSideWatch("app", { watching: ["../discourse-markdown-it"] })
)
app: RawHandlebarsCompiler(
withSideWatch("app", { watching: ["../discourse-markdown-it"] })
),
},
});
if (EMBER_MAJOR_VERSION < 4) {
// TODO: remove me
// Ember 3.28 still has some internal dependency on jQuery being a global,
// for the time being we will bring it in vendor.js
app.import("node_modules/jquery/dist/jquery.js", { prepend: true });
}
// WARNING: We should only import scripts here if they are not in NPM.
app.import(vendorJs + "bootbox.js");
app.import(discourseRoot + "/app/assets/javascripts/polyfills.js");
@ -186,8 +160,7 @@ module.exports = function (defaults) {
if (
!request.includes("-embroider-implicit") &&
// TODO: delete special case for jquery when removing app.import() above
((EMBER_MAJOR_VERSION < 4 && request === "jquery") ||
request.startsWith("admin/") ||
(request.startsWith("admin/") ||
request.startsWith("discourse/plugins/") ||
request.startsWith("discourse/theme-"))
) {

View File

@ -26,30 +26,3 @@ define("ember-addons/ember-computed-decorators", [
);
return decorators;
});
// Based on https://github.com/emberjs/ember-jquery-legacy
// The addon has out-of-date dependences, but it's super simple so we can reproduce here instead:
define("ember-jquery-legacy", ["exports"], function (exports) {
exports.normalizeEvent = function (e) {
if (e instanceof Event) {
return e;
}
// __originalEvent is a private escape hatch of Ember's EventDispatcher to allow accessing `originalEvent` without
// triggering a deprecation message.
return e.__originalEvent || e.originalEvent;
};
});
// ember-cached-decorator-polyfill uses a Babel transformation to apply this polyfill in core.
// Adding that Babel transformation to themes and plugins will be complex, so we use this to
// patch it at runtime. This can be removed once `@glimmer/tracking` is updated to a version
// with native `@cached` support.
const glimmerTracking = require("@glimmer/tracking");
if (glimmerTracking.cached) {
// No-op. Can be removed once we're fully upgraded to Ember 4+
// Search juice: EMBER_MAJOR_VERSION < 4;
} else {
Object.defineProperty(glimmerTracking, "cached", {
get: () => require("ember-cached-decorator-polyfill").cached,
});
}

View File

@ -1,34 +0,0 @@
{
"private": true,
"scripts": {
"postinstall": "./run-patch-package"
},
"workspaces": [
"admin",
"bootstrap-json",
"deprecation-silencer",
"dialog-holder",
"discourse",
"discourse-common",
"discourse-hbr",
"discourse-i18n",
"discourse-markdown-it",
"discourse-plugins",
"discourse-widget-hbs",
"ember-cli-progress-ci",
"ember-production-deprecations",
"float-kit",
"pretty-text",
"select-kit",
"theme-transpiler",
"truth-helpers"
],
"resolutions": {
"**/unset-value": "2.0.1",
"**/ember-source": "3.28.12"
},
"devDependencies": {
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0"
}
}

View File

@ -1 +0,0 @@
package-ember5.json

View File

@ -0,0 +1 @@
package-ember5.json

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
yarn-ember5.lock

View File

@ -0,0 +1 @@
yarn-ember5.lock

View File

@ -157,8 +157,7 @@ class AdminDashboardData
:unreachable_themes,
:watched_words_check,
:google_analytics_version_check,
:translation_overrides_check,
:ember_version_check
:translation_overrides_check
add_problem_check { sidekiq_check || queue_size_check }
end
@ -382,10 +381,6 @@ class AdminDashboardData
nil
end
def ember_version_check
I18n.t("dashboard.ember_version_warning") if ENV["EMBER_VERSION"] == "3"
end
def out_of_date_themes
old_themes = RemoteTheme.out_of_date_themes
return unless old_themes.present?

View File

@ -56,17 +56,6 @@ if !args.include?("test") && !args.include?("build") && !args.include?("--proxy"
args << PROXY
end
if !["3", "5", nil].include?(ENV["EMBER_VERSION"])
raise "Unknown ember version #{ENV["EMBER_VERSION"]}"
end
system(
"script/switch_ember_version",
ENV["EMBER_VERSION"] || "5",
exception: true,
chdir: RAILS_ROOT,
)
# Running yarn install in the root directory will also run it for YARN_DIR via a post-install hook
exit 1 if !system "yarn", "-s", "install", "--cwd", RAILS_ROOT

View File

@ -1537,7 +1537,6 @@ en:
watched_word_regexp_error: "The regular expression for '%{action}' watched words is invalid. Please check your <a href='%{base_path}/admin/customize/watched_words'>Watched Word settings</a>, or disable the 'watched words regular expressions' site setting."
v3_analytics_deprecated: "Your Discourse is currently using Google Analytics 3, which will no longer be supported after July 2023. <a href='https://meta.discourse.org/t/260498'>Upgrade to Google Analytics 4</a> now to continue receiving valuable insights and analytics for your website's performance."
category_style_deprecated: "Your Discourse is currently using a deprecated category style which will be removed before the final beta release of Discourse 3.2. Please refer to <a href='https://meta.discourse.org/t/282441'>Moving to a Single Category Style Site Setting</a> for instructions on how to keep your selected category style."
ember_version_warning: "Your site is running with the unsupported EMBER_VERSION=3 configuration. This may lead to unexpected issues with themes/plugins. Please remove the EMBER_VERSION configuration from your app.yml file and rebuild. See <a href='https://meta.discourse.org/t/287211'>https://meta.discourse.org/t/287211</a> for more information."
back_from_logster_text: "Back to site"
site_settings:

View File

@ -10,13 +10,7 @@ task "assets:precompile:build" do
if ENV["SKIP_EMBER_CLI_COMPILE"] != "1"
ember_version = ENV["EMBER_VERSION"] || "5"
raise "Unknown ember version '#{ember_version}'" if !%w[3 5].include?(ember_version)
if ember_version == "3"
puts "Downgrading to Ember 3..."
system("script/switch_ember_version", ember_version, exception: true, chdir: Rails.root)
system("yarn install", exception: true, chdir: "app/assets/javascripts/discourse")
end
raise "Unknown ember version '#{ember_version}'" if !%w[5].include?(ember_version)
compile_command = "CI=1 yarn --cwd app/assets/javascripts/discourse run ember build"

View File

@ -1,16 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "fileutils"
# rubocop:disable Discourse/NoChdir
Dir.chdir("#{__dir__}/../app/assets/javascripts") do
FileUtils.rm("yarn-ember3.lock")
FileUtils.cp("yarn-ember5.lock", "yarn-ember3.lock")
system("#{__dir__}/switch_ember_version", "3", exception: true)
system "yarn install", exception: true
system("#{__dir__}/switch_ember_version", "5", exception: true)
end

View File

@ -1,17 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "fileutils"
v = ARGV[0]
raise "Unexpected version #{v}" if !%w[3 5].include?(v)
# rubocop:disable Discourse/NoChdir
Dir.chdir("#{__dir__}/../app/assets/javascripts") do
FileUtils.rm("package.json")
FileUtils.rm("yarn.lock")
File.symlink("package-ember#{v}.json", "package.json")
File.symlink("yarn-ember#{v}.lock", "yarn.lock")
end