I am using ember-remarkable because I want to add both markdown parsing and syntax highlighting to my project. The only thing it's missing is line numbers, which I want to add by using this library: highlightjs-line-numbers.js
I override ember-remarkable's md-text component with the following:
import MDTextComponent from 'ember-remarkable/components/md-text';
export default MDTextComponent.extend({
tagName: null,
didRender() {
this._super(...arguments);
this.$("code[class*='language-']").each((i, block) => {
this.$(block).addClass('hljs');
hljs.lineNumbersBlock(block);
});
}
});
Everything works! However, my editor complains that hljs is not defined, even though it is available on the page.
How do I avoid this error?
If it is ES6 modules, just import it. Otherwise you can use it as window.hljs.
When you run your js code in browser, window is the root of the global variables. So you can access it as window.hljs.
If you run your code in Fastboot, this should be a better way to access global objects: (window || Fastboot || global).hljs.
On the other hand, instead of using the global object container, you can just prevent linter to give this error. For eslint, see this configuration: Specifying Globals.
You should explicitly import your dependencies, if for no other reason than to communicate to future readers. import hljs from 'highlightjs-line-numbers'
Related
I have a TypeScript application working with es16 modules, most of them are imported statically. I want to use a (validator-) module now that is only imported in debug mode. It's all working, but I don't understand how to type things so that I get code completion and error-checking.
in my main class in main.ts I have:
...
if(debug){
import('./validator.js').then((module) => this.validate(module))
}
...
the validate method looks like that:
private validate(module):void{
new module.Validator(dataToValidate);
}
validator.js contains:
export class Validator{
coonstructor(data:MyDatatype){
stuff going on here...
}
}
what I would like to know/do is:
in the validate method:
private validate(module:someMeaningfulType){...}
and also I'd like to import the Validator class, without actually importing it.
if I wrote
import {Validator} from './validate.ts'
at the top of main.ts I would load the file regardles of I need it, which defeats the whole point of dynamic imports.
I might try to whrite a type declartaion for module and Validator in main.ts, but even if that wouldn't conflict somehow, I would have to manually keep it in sync with the actual module, which is not what I want - obviously.
I might miss something obvious, but I cannot find out what. I find id hard to search for the (pure) use of native es2020/2022 modules with Typescrit, as there is so much information about node-modules etc. overshadowing it.
You can actually use import with typeof to get the type of the imported module:
private validate(module: typeof import("./validator.js")) { ... }
Alternatively, you can use a type-only import, which will be erased in the output:
import type * as ValidatorModule from "./validator.js";
// ...
private validate(module: ValidatorModule) { ... }
Prior to ES6 modules, it was (I'm told by other Stack answers) easy to force a JS script to be reloaded, by deleting its require cache:
delete require.cache[require.resolve('./mymodule.js')]
However, I can't find an equivalent for ES6 modules loaded via import.
That might be enough to make this question clear, but just in case, here's a simplified version of the code. What I have is a node server running something like:
-- look.mjs --
var look = function(user) { console.log(user + " looks arond.") }
export { look };
-- parser.mjs --
import { look } from './look.mjs';
function parse(user, str) {
if (str == "look") return look(user);
}
What I want is to be able to manually change the look.mjs file (e.g. to fix a misspelled word), trigger a function that causes look.mjs to be reimported during runtime, such that parse() returns the new value without having to restart the node server.
I tried changing to dynamic import, like this:
-- parser.mjs --
function parse(user, str) {
if (str == "look") {
import('./look.mjs').then(m => m.look(user))
}
}
This doesn't work either. (I mean, it does, but it doesn't reload look.mjs each time it's called, just on the first time) And I'd prefer to keep using static imports if possible.
Also, in case this is not clear, this is all server side. I'm not trying to pass a new module to the client, just get one node module to reload another node module.
I don't know what the reason behind doing this,
I think this is not safe to change the context of modules at runtime and cause unexpected behaviors and this is one of the reasons that Deno came to.
If you want to run some code evaluation at runtime you can use something like this using vm:
https://nodejs.org/dist/latest-v16.x/docs/api/vm.html
You could try using nodemon to dynamically refresh when you make code changes
https://www.npmjs.com/package/nodemon
I agree with #tarek-salem that it's better to use vm library. But there is another way to solve your problem.
There is no way to clear the dynamic import cache which you use in question (btw there is a way to clear the common import cache because require and common import has the same cache and the dynamic import has its own cache). But you can use require instead of dynamic import. To do it first create require in parser.mjs
import Module from "module";
const require = Module.createRequire(import.meta.url);
Then you have 2 options:
Easier: convert look.mjs into commonjs format (rename it look.cjs and use module.exports).
If want to make it possible to either import AND require look.mjs you should create the npm package with package.json
{
"main": "./look.cjs",
"type": "commonjs"
}
In this case in parser.mjs you will be able to use require('look') and in other files import('look') or import * as look from 'look'.
It's hard to describe so I create a repo: https://github.com/galenyuan/how-to-retyping
I want to do like this:
import Vue from 'vue'
declare module 'vuex/types/vue' {
interface Vue {
$store: number
}
}
which Vue is defined in vue/types/vue and vuex retyping it in vuex/types/vue, so how can I custom it again?
Firstly, all module augmentations have to target the actual module that declares what is being augmented. This can be observed in the vuex augmentation itself.
// vuex/types/vue.d.ts
declare module "vue/types/vue" {
interface Vue {
$store: Store<any>;
}
}
You need to do the same. That is, in your types/store.d.ts you would write
export {} // ensure this file is a module
declare module "vue/types/vue" {
interface Vue {
$store: Store<any>;
}
}
That will work, which is to say that it will be seen as an augmentation of Vue. Unfortunately we will immediately hit another error, one for which do not know of a workaround.
Subsequent variable declarations must have the same type. Variable '$store' has type 'Store' at `*****/how-to-retyping-master/node_modules/vuex/types/vue.d.ts 15:4, but here has type 'number'.
This is a limitation of declaration merging, overlapping member declarations conflict.
There is not a good way around this because importing "vuex" prevents us from excluding anything it imports, and that means we cannot exclude the augmentation that ships with it.
We could redirect our import of "vuex" using paths, or shadow it with an ambient external module declaration, but that would require us to redeclare everything in "vuex/types/index.d.ts" and "vuex/types/helpers.d.ts" which is not practical or maintainable.
Is there any way to have webpack execute module in the global scope? Specifically, my use case is the following library:
https://github.com/AzureAD/azure-activedirectory-library-for-js/blob/master/lib/adal.js
where there is the following code:
var AuthenticationContext;
if (typeof module !== 'undefined' && module.exports) {
module.exports.inject = function (conf) {
return new AuthenticationContext(conf);
};
}
As you can see the module is exporting an injection function (not sure why they don't just export the class). I am able to construct a new AuthenticationContext object successfully using the using the injection function. However, some of the functionality in this library relies on a global AuthenticationContext class and it errors out when window.AuthenticationContext === undefined. I would like to bundle this module with webpack but somehow, I need to ensure that AuthenticationContext will be available in the global scope. Is there any way to do that?
I have read about the ProvidePlugin but, as I understand the ProvidePlugin just takes an exported value and attaches it to the global scope. In this case I need to ensure that a non-exported value will be available in the global scope.
The most obvious solution is just to execute this module in the global scope. However, I would like this module to be part of the bundle. How can I accomplish this?
Thanks in advance.
I think I solved this with the following code:
import {inject} from 'adal-angular/lib/adal.js';
import config from './auth-config';
export default class Authenticator {
constructor() {
this.authContext = inject(config);
window.AuthenticationContext = this.authContext.constructor;
}
}
basically, I am exposing the constructor manually.
I am experiencing a really weird behavior and can't even say which package to blame for it.
My setup: RequireJS project with the JSXTransformer and the jsx! plugin
I have an es6 class like this:
define([
'react'
], function(
React
) {
class MyComponent extends React.Component {
myMethod() {
otherObject.someMethod()._privateProp; // Yes, we need this accessing and have no influence on it
}
}
return MyComponent;
});
The transpiled output in the resulting bundle after running r.js is:
define('jsx!project/components/InputOutput',[
'react'
], function(
React
) {
var ____Class8=React.Component;for(var ____Class8____Key in ____Class8){if(____Class8.hasOwnProperty(____Class8____Key)){MyComponent[____Class8____Key]=____Class8[____Class8____Key];}}var ____SuperProtoOf____Class8=____Class8===null?null:____Class8.prototype;MyComponent.prototype=Object.create(____SuperProtoOf____Class8);MyComponent.prototype.constructor=MyComponent;MyComponent.__superConstructor__=____Class8;function MyComponent(){"use strict";if(____Class8!==null){____Class8.apply(this,arguments);}}
MyComponent.prototype.myMethod=function() {"use strict";
otherObject.someMethod().$MyComponent_privateProp;
};
return MyComponent;
});
Note how otherObject.someMethod().$MyComponent_privateProp; is written there. This obviously breaks because it is not a property on instances of MyComponent.
Add /** #preventMunge */ to the top of the file. See this GitHub issue:
Yes, sorry this is a non-standard fb-ism. For now you can work around this and toggle this feature off by putting /** #preventMunge */ at the top of your file -- but that's also a pretty big fb-ism. We should (a) turn this into a transform option (rather than a direct docblock directive) and (b) make it opt-in rather than opt-out (since it's non-standard).
For context: We munge all under-prefixed object properties on a per-module basis partly because our under-prefix convention applies to both objects and classes. Additionally, even if we wanted to lax the objects vs classes distinction, it's impossible to tell (in the general case) if a property is a reference to this since alias variables can occur (i.e. var self = this; self._stuff;).