diff --git a/.eslintrc b/.eslintrc index 6524084..3118ddb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,8 @@ { "extends": "eslint-config-discourse", + "ignorePatterns": ["javascripts/vendor/*"], "globals": { - "themePrefix": "on" + "settings": "readonly", + "themePrefix": "readonly" } } diff --git a/.github/workflows/component-linting.yml b/.github/workflows/component-linting.yml new file mode 100644 index 0000000..2385132 --- /dev/null +++ b/.github/workflows/component-linting.yml @@ -0,0 +1,48 @@ +name: Linting + +on: + push: + branches: + - main + pull_request: + +concurrency: + group: plugin-linting-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: yarn + + - name: Yarn install + run: yarn install + + - name: ESLint + if: ${{ always() }} + run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,javascripts} + + - name: Prettier + if: ${{ always() }} + shell: bash + run: | + yarn prettier -v + files=$(find javascripts desktop mobile common scss -type f \( -name "*.scss" -or -name "*.js" -or -name "*.es6" \) 2> /dev/null) || true + if [ -n "$files" ]; then + yarn prettier --list-different $files + fi + if [ 0 -lt $(find test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then + yarn prettier --list-different "test/**/*.{js,es6}" + fi + + - name: Ember template lint + if: ${{ always() }} + run: yarn ember-template-lint --no-error-on-unmatched-pattern javascripts diff --git a/.github/workflows/component-tests.yml b/.github/workflows/component-tests.yml new file mode 100644 index 0000000..944aa1e --- /dev/null +++ b/.github/workflows/component-tests.yml @@ -0,0 +1,147 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + +concurrency: + group: plugin-tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + outputs: + tests_exist: ${{ steps.check_tests.outputs.tests_exist }} + + steps: + - name: Install component + uses: actions/checkout@v3 + with: + path: tmp/component + fetch-depth: 1 + + - name: Check QUnit existence + id: check_tests + shell: bash + run: | + if [ 0 -lt $(find tmp/component/test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then + echo "::set-output name=tests_exist::true" + fi + + test: + needs: check + if: ${{ needs.check.outputs.tests_exist }} + runs-on: ubuntu-latest + container: discourse/discourse_test:slim-browsers + timeout-minutes: 15 + + env: + DISCOURSE_HOSTNAME: www.example.com + RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072 + RAILS_ENV: development + PGUSER: discourse + PGPASSWORD: discourse + + steps: + - uses: actions/checkout@v3 + with: + repository: discourse/discourse + fetch-depth: 1 + + - name: Install component + uses: actions/checkout@v3 + with: + path: tmp/component + fetch-depth: 1 + + - name: Setup Git + run: | + git config --global user.email "ci@ci.invalid" + git config --global user.name "Discourse CI" + + - name: Start redis + run: | + redis-server /etc/redis/redis.conf & + + - name: Start Postgres + run: | + chown -R postgres /var/run/postgresql + sudo -E -u postgres script/start_test_db.rb + sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';" + + - name: Bundler cache + uses: actions/cache@v3 + with: + path: vendor/bundle + key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gem- + + - name: Setup gems + run: | + gem install bundler --conservative -v $(awk '/BUNDLED WITH/ { getline; gsub(/ /,""); print $0 }' Gemfile.lock) + bundle config --local path vendor/bundle + bundle config --local deployment true + bundle config --local without development + bundle install --jobs 4 + bundle clean + + - name: Lint English locale + run: bundle exec ruby script/i18n_lint.rb "tmp/component/locales/en.yml" + + - name: Get yarn cache directory + id: yarn-cache-dir + run: echo "::set-output name=dir::$(yarn cache dir)" + + - name: Yarn cache + uses: actions/cache@v3 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Yarn install + run: yarn install + + - name: Fetch app state cache + uses: actions/cache@v3 + id: app-cache + with: + path: tmp/app-cache + key: >- + ${{ hashFiles('.github/workflows/tests.yml') }}- + ${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}- + + - name: Restore database from cache + if: steps.app-cache.outputs.cache-hit == 'true' + run: psql -f tmp/app-cache/cache.sql postgres + + - name: Restore uploads from cache + if: steps.app-cache.outputs.cache-hit == 'true' + run: rm -rf public/uploads && cp -r tmp/app-cache/uploads public/uploads + + - name: Create and migrate database + if: steps.app-cache.outputs.cache-hit != 'true' + run: | + bin/rake db:create + bin/rake db:migrate + + - name: Dump database for cache + if: steps.app-cache.outputs.cache-hit != 'true' + run: mkdir -p tmp/app-cache && pg_dumpall > tmp/app-cache/cache.sql + + - name: Dump uploads for cache + if: steps.app-cache.outputs.cache-hit != 'true' + run: rm -rf tmp/app-cache/uploads && cp -r public/uploads tmp/app-cache/uploads + + - name: Component QUnit + run: | + THEME_NAME=$(ruby -e 'require "json"; puts JSON.parse(File.read("tmp/component/about.json"))["name"]') + bundle exec rake themes:install -- "--{\"$THEME_NAME\": \"tmp/component\"}" + UNICORN_TIMEOUT=120 bundle exec rake "themes:qunit[name,$THEME_NAME]" + timeout-minutes: 10 diff --git a/.gitignore b/.gitignore index 6b7cdf1..14735c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -.discourse-site -HELP node_modules +.discourse-site diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/.template-lintrc.js b/.template-lintrc.js new file mode 100644 index 0000000..a558b8e --- /dev/null +++ b/.template-lintrc.js @@ -0,0 +1,4 @@ +module.exports = { + plugins: ["ember-template-lint-plugin-discourse"], + extends: "discourse:recommended", +}; diff --git a/about.json b/about.json index 46531cb..8911946 100644 --- a/about.json +++ b/about.json @@ -1,6 +1,6 @@ { "name": "discourse-placeholder-theme-component", + "component": true, "about_url": "https://github.com/discourse/discourse-placeholder-theme-component", - "license_url": "https://github.com/discourse/discourse-placeholder-theme-component/blob/main/LICENSE", - "component": true + "license_url": "https://github.com/discourse/discourse-placeholder-theme-component/blob/main/LICENSE" } diff --git a/javascripts/discourse/controllers/discourse-placeholder-builder.js b/javascripts/discourse/controllers/discourse-placeholder-builder.js index 697f172..6063c28 100644 --- a/javascripts/discourse/controllers/discourse-placeholder-builder.js +++ b/javascripts/discourse/controllers/discourse-placeholder-builder.js @@ -2,6 +2,8 @@ import Controller from "@ember/controller"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import EmberObject, { action } from "@ember/object"; import { isBlank } from "@ember/utils"; +import I18n from "I18n"; +import bootbox from "bootbox"; export default Controller.extend(ModalFunctionality, { form: null, diff --git a/javascripts/discourse/initializers/setup.js b/javascripts/discourse/initializers/setup.js index 6e28d40..05b7de0 100644 --- a/javascripts/discourse/initializers/setup.js +++ b/javascripts/discourse/initializers/setup.js @@ -1,6 +1,6 @@ import showModal from "discourse/lib/show-modal"; import { withPluginApi } from "discourse/lib/plugin-api"; -import { later, debounce } from "@ember/runloop"; +import { debounce, later } from "@ember/runloop"; import cookie, { removeCookie } from "discourse/lib/cookie"; const VALID_TAGS = @@ -112,7 +112,7 @@ export default { key: `${STORAGE_PREFIX}${key}`, value: { expires: Date.now() + EXPIRE_AFTER_SECONDS, - value: value, + value, }, }); }, @@ -130,7 +130,9 @@ export default { withPluginApi("0.8.7", (api) => { api.decorateCookedElement( (cooked, postWidget) => { - if (!postWidget) return; + if (!postWidget) { + return; + } const postIdentifier = `${postWidget.widget.attrs.topicId}-${postWidget.widget.attrs.id}-`; const mappings = []; @@ -160,7 +162,9 @@ export default { cooked.querySelectorAll(VALID_TAGS).forEach((elem, index) => { const mapping = mappings[index]; - if (!mapping) return; + if (!mapping) { + return; + } let diff = 0; let replaced = false; @@ -190,7 +194,9 @@ export default { diff = diff + newValue.length - previousLength; }); - if (replaced) elem.innerHTML = newInnnerHTML; + if (replaced) { + elem.innerHTML = newInnnerHTML; + } }); }; @@ -252,7 +258,9 @@ export default { placeholderNodes.forEach((elem) => { const dataKey = elem.dataset.key; - if (!dataKey) return; + if (!dataKey) { + return; + } const placeholderIdentifier = `${postIdentifier}${dataKey}`; const valueFromStore = this.getValue(placeholderIdentifier); diff --git a/javascripts/discourse/templates/modal/discourse-placeholder-builder.hbs b/javascripts/discourse/templates/modal/discourse-placeholder-builder.hbs index c2127dd..a3d0334 100644 --- a/javascripts/discourse/templates/modal/discourse-placeholder-builder.hbs +++ b/javascripts/discourse/templates/modal/discourse-placeholder-builder.hbs @@ -1,7 +1,8 @@ {{#d-modal-body title=(theme-prefix "builder.title") class="discourse-placeholder-builder" - style="overflow: auto"}} + style="overflow: auto" +}}