mardi 17 janvier 2023

ember-bootstrap with LESS -- application css not transpiling

I'm working on upgrading little-by-little an EmberJS Application that uses ember-bootstrap. Before moving to Ember 4 we are trying to move up to the Ember 3 LTS 3.28.6. However, we have ember-bootstrap using LESS in our project and things have fallen apart a bit because of some dependency mapping that pushes us to newer versions. To avoid migrating to SASS, I'm currently trying to use https://github.com/seanCodes/bootstrap-less-port to keep less for one last iteration. But a curious thing is happening in my local build: the application css is not being generated (.css) is nowhere to be found in dist/assets/ !

The build seems to go just fine (save the long list of deprecation errors).

Anyone have an idea why my appli.css is not generating ? How would I specify to ember-bootstrap to look for appli.less ? Is this possible ? If it is, it seems the code is here: https://github.com/ember-bootstrap/ember-bootstrap/blob/d98e054abd8ea172bbd47a81655ef300aeae7f87/blueprints/ember-bootstrap/index.js#L103 Maybe I'm being blocked here? https://github.com/ember-bootstrap/ember-bootstrap/blob/d98e054abd8ea172bbd47a81655ef300aeae7f87/blueprints/ember-bootstrap/index.js#L57

package.json

{
  "name": "appli",
  "version": "1.0.0",
  "private": true,
  "description": "Small description for appli goes here",
  "repository": "",
  "license": "MIT",
  "author": "",
  "directories": {
    "doc": "doc",
    "test": "tests"
  },
  "scripts": {
    "build": "ember build",
    "lint:hbs": "ember-template-lint .",
    "lint:js": "eslint .",
    "start": "ember serve",
    "test": "ember test"
  },
  "devDependencies": {
    "@ember/jquery": "^1.1.0",
    "@ember/optional-features": "^2.0.0",
    "@ember/test-helpers": "^2.6.0",
    "@glimmer/component": "^1.0.4",
    "@glimmer/tracking": "^1.0.4",
    "babel-eslint": "^10.1.0",
    "bootstrap": "^4.6.0",
    "bootstrap-less-port": "^2.5.1",
    "bootstrap-datepicker": "1.4.0",
    "bootstrap-select": "1.7.4",
    "broccoli-asset-rev": "^3.0.0",
    "browserslist": "^4.21.0",
    "codemirror": "^5.50.2",
    "ember-ajax": "^5.0.0",
    "ember-auto-import": "^1.12.0",
    "ember-bootstrap": "^4.9.0",
    "ember-cli": "^3.28.6",
    "ember-cli-app-version": "^5.0.0",
    "ember-cli-babel": "^7.26.10",
    "ember-cli-dependency-checker": "^3.2.0",
    "ember-cli-file-saver": "^1.2.4",
    "ember-cli-htmlbars": "^5.7.2",
    "ember-cli-inject-live-reload": "^2.1.0",
    "ember-cli-sri": "^2.1.1",
    "ember-cli-terser": "^4.0.2",
    "ember-copy": "^2.0.1",
    "ember-data": "^3.28.6",
    "ember-export-application-global": "^2.0.1",
    "ember-fetch": "^8.1.1",
    "ember-load-initializers": "^2.1.2",
    "ember-maybe-import-regenerator": "^0.1.6",
    "ember-moment": "^9.0.1",
    "ember-page-title": "^6.2.2",
    "ember-qunit": "^5.1.5",
    "ember-resolver": "^8.0.3",
    "ember-source": "~3.28.8",
    "ember-template-lint": "^3.15.0",
    "ember-welcome-page": "^4.1.0",
    "eslint": "^7.32.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-ember": "^10.5.8",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-prettier": "^3.4.1",
    "eslint-plugin-qunit": "^6.2.0",
    "handlebars": "3.0.3",
    "loader.js": "^4.7.0",
    "moment-duration-format": "2.2.2",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.5.1",
    "qunit": "^2.17.2",
    "qunit-dom": "^1.6.0",
    "select2": "4.0.4",
    "underscore": "1.9.1",
    "vis": "4.21.0"
  },
  "engines": {
    "node": "12.* || 14.* || >= 16"
  },
  "dependencies": {
    "datatables.net-dt": "^1.10.20",
    "datatables.net-plugins": "^1.10.20",
    "datatables.net-rowgroup-dt": "^1.1.1",
    "less": "^3.13.1"
  },
  "ember": {
    "edition": "octane"
  }
}

ember-cli-build.js

'use strict';

const EmberApp = require('ember-cli/lib/broccoli/ember-app');
var Funnel = require('broccoli-funnel');

module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
    'ember-bootstrap': {
      bootstrapVersion: 4,
      importBootstrapCSS: false,

    },
    'lessOptions': {
      paths: ['node_modules/bootstrap/less'],
    }
  });

  ...

  return app.toTree([imgJquery]);

app.less

@import "bootstrap-less-port";
@import "theme";
@import "variables";
@import "panels";
@import (less) "style.css";
@import (less) "datatable.css";
@import (less) "charts.css";
@import (less) "select2.css";
@import (less) "timeline.css";
@import (less) "codemirror.css";
@import (less) "vis-helios.css";

Action: Running

npm run build

Expected: should generate the appli.css and vendor.css files within dist/assets

Observed: app.less and app.css.map are present vendor.css is present




vendredi 13 janvier 2023

Carousel between string and non-string version of package in my yarn.lock

I've had this issue for a long time and I'm finally done with it! I've got a single package in my app that, after each time yarn [install] is run it switches from the string version to the non-string version and vise versa in the yarn.lock file. If I check in the change and commit it, the next time I yarn, it switches back.

Here's the example

+ "ember-phone-input@github:cph/ember-phone-input#cph-modification":
- ember-phone-input@cph/ember-phone-input.git#cph-modification:

Anyone know of any settings to add to package.json do end this senseless psychic damage I'm taking 🙂




dimanche 8 janvier 2023

Mirage server GETs data but POST fails

I have the mirage models:

// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';

export default Model.extend({
    name: '',
    iso3166_1_alpha3: '',
    capitol_city: belongsTo('city', {inverse: null}),
    cities: hasMany('city', {inverse: 'country'})
});

and:

// mirage/models/city.js
import { Model, belongsTo } from 'miragejs';

export default Model.extend({
    name: '',
    country: belongsTo('country', {inverse: 'cities'})
});

and the serializer:

// mirage/serializers/application.js
import { camelize, capitalize, underscore } from '@ember/string';
import { JSONAPISerializer } from 'miragejs';

export default class ApplicationSerializer extends JSONAPISerializer
{
    alwaysIncludeLinkageData = true;

    keyForAttribute(attr) {
        return underscore(attr);
    };
    keyForRelationship(modelName) {
        return underscore(modelName);
    };
    typeKeyForModel(model) {
        return capitalize(camelize(model.modelName));
    };
};

When I run the tests:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';

module('Unit | Mirage | mirage models', function (hooks) {
  setupTest(hooks);
  setupMirage(hooks);

  test('it retrieves the country', async function (assert) {
    const server = this.server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    server.create(
        'country',
        {
            id: 'FR',
            name: 'France',
            iso3166_1_alpha3: 'FRA',
            capitol_city: city
        }
    );

    let response = await fetch('/api/countries')
    assert.strictEqual(response.status, 200, "Should have created the model");
    let json = await response.json();
    assert.deepEqual(
        json,
        {
            data: [
                {
                    type: 'Country',
                    id: 'FR',
                    attributes: {
                        name: 'France',
                        iso3166_1_alpha3: 'FRA',
                    },
                    relationships: {
                        capitol_city: {data: {type: 'City', id: '1'}},
                        cities: {data: []},
                    }
                }
            ]
        }
    )
  });

  test('it creates the country', async function (assert) {
    const server = this.server;
    server.create('city', { id: '1', name: 'Paris' });

    let response = await fetch(
        '/api/countries',
        {
            method: 'POST',
            headers: {'Countent-Type': 'application/json'},
            body: JSON.stringify(
                {
                    data: {
                        id: 'FR',
                        type: 'Country',
                        attributes: {
                            iso3166_1_alpha3: 'FRA',
                            name: 'France',

                        },
                        relationships: {
                            capitol_city: { data: { type: 'City', id: '1'} },
                            cities: { data: [{ type: 'City', id: '1'}] }
                        }
                    }
                }
            )
        }
    );

    console.log((await response.json()).message);
    assert.strictEqual(response.status, 201, "Should have created the model");
  });
});

The first one passes and the second one fails with the message:

Mirage: You're passing the relationship 'capitol_city' to the 'country' model via a POST to '/api/countries', but you did not define the 'capitol_city' association on the 'country' model.

How can I get Mirage to recognise the capitol_city attribute on the model?




samedi 24 décembre 2022

cordovaProj.create is not a function with Emberjs + corber

I'm trying to add corber to my existing EmberJS application. When I run corber init then I get

stack: TypeError: cordovaProj.create is not a function at Class.run (/Users/denisk/.nvm/versions/node/v18.12.1/lib/node_modules/corber/lib/targets/cordova/tasks/create-project.js:33:26) at /Users/denisk/.nvm/versions/node/v18.12.1/lib/node_modules/corber/lib/tasks/create-project.js:97:24 at tryCatcher (/Users/denisk/.nvm/versions/node/v18.12.1/lib/node_modules/corber/node_modules/rsvp/dist/rsvp.js:326:21) at invokeCallback (/Users/denisk/.nvm/versions/node/v18.12.1/lib/node_modules/corber/node_modules/rsvp/dist/rsvp.js:498:33) at publish (/Users/denisk/.nvm/versions/node/v18.12.1/lib/node_modules/corber/node_modules/rsvp/dist/rsvp.js:484:9) at flush (/Users/denisk/.nvm/versions/node/v18.12.1/lib/node_modules/corber/node_modules/rsvp/dist/rsvp.js:2441:7) at process.processTicksAndRejections (node:internal/process/task_queues:77:11)

My env is:

ember-cli: 4.9.2 node: 18.12.1

How can I add corber and build mobile app with Ember?




lundi 19 décembre 2022

How do i get the ember-concurrency ajax-throttling example to work in my dev environment?

My question is, what do i need to fix so that my implementation of the ember-concurrency Ajax Throttling example works as expected. Specifically, in my implementation no log entries are displayed (as they are on the example page), and the console.log statement that i added in the loopingAjaxTask inner function is not executed nor is the console.log statement in the task. I have copied the example nearly verbatim with the only changes being that i've added in those console.log statements.

The line 'looping Ajax task outer function' is written to the console 8 times as expected, but the 'looping Ajax task inner function' is never written to the console log. The 'Hello World' text from the component is written to the page.

I am using ember 4.8 and ember-concurrency 2.3.7.

Here's the template where i've included the AjaxThrottling component.

app/templates/example.hbs


<p>Here's where the log entries should appear</p>
<AjaxThrottling />
 

app/components/ajax-throttling.hbs

<div>
    hello world
  <ul>
    
      <li style= ></li>
    
  </ul>
</div>

and here is component's js page

import Component from '@glimmer/component';
import { enqueueTask, task, timeout } from 'ember-concurrency';

function loopingAjaxTask(id, color) {
    console.log('looping Ajax task outer function');
    return function* () {
        console.log('looping Ajax task inner function');
        while (true) {
        this.log(color, `Task ${id}: making AJAX request`);
        yield this.ajaxTask.perform();
        this.log(color, `Task ${id}: Done, sleeping.`);
        yield timeout(2000);
      }
    };
  }

export default class AjaxThrottlingComponent extends Component {

    tagName = '';
    logs = [];
  
    ajaxTask = enqueueTask({ maxConcurrency: 3 }, async () => {
      // simulate slow AJAX
      console.log('inside the task')
      await timeout(2000 + 2000 * Math.random());
      return {};
    });
  
    @task({ on: 'init' }) task0 = loopingAjaxTask(0, '#0000FF');
    @task({ on: 'init' }) task1 = loopingAjaxTask(1, '#8A2BE2');
    @task({ on: 'init' }) task2 = loopingAjaxTask(2, '#A52A2A');
    @task({ on: 'init' }) task3 = loopingAjaxTask(3, '#DC143C');
    @task({ on: 'init' }) task4 = loopingAjaxTask(4, '#20B2AA');
    @task({ on: 'init' }) task5 = loopingAjaxTask(5, '#FF1493');
    @task({ on: 'init' }) task6 = loopingAjaxTask(6, '#228B22');
    @task({ on: 'init' }) task7 = loopingAjaxTask(7, '#DAA520');
  
    log(color, message) {
      let logs = this.logs;
      logs.push({ color, message });
      this.set('logs', logs.slice(-7));
    }
}




vendredi 16 décembre 2022

Specifying a different template path for an ember component

Suppose I have this structure:

modals/
├─ index.hbs
├─ base.ts
├─ new/
│  ├─ index.ts (extends base.ts)
├─ rename/
│  ├─ index.ts (extends base.ts)

(Basically I have two components which have exactly the same template, but different logic within the .ts/.js files)

Is it possible to specify the template path in both of those index.ts files to point to the index.hbs which is located one level above?

Or something similar, the idea is to not have to duplicate the template between those two components.

I've tried the structure above but it does not seem to work. I imagine I must specify a template path somewhere but I just can't find something like that in the documentation.




Is `store.find` function different to `store.findRecord` function on Ember JS?

There are two similar functions on Ember JS service "store". In this example they work alike.

find function

  @service store;

  async model(params) {
    return this.store.find('rental', params.rental_id);
  }

findRecord function

  @service store;

  async model(params) {
    return this.store.findRecord('rental', params.rental_id);
  }

Are they different? Or just an alias?