I installed a npm package which contains a javascript file, what I want to use. The js file name is all.js and contains this code:
import { initExtended } from './extended'
import Header from './components/header/header'
function initAll(options) {
// Set the options to an empty object by default if no options are passed.
options = typeof options !== 'undefined' ? options : {}
// Allow the user to initialise GOV.UK Frontend in only certain sections of the page
// Defaults to the entire document if nothing is set.
var scope = typeof options.scope !== 'undefined' ? options.scope : document
// Find first header module to enhance.
var $toggleButton = scope.querySelector('[data-module="govuk-header"]')
new Header($toggleButton).init()
initExtended(options)
}
export {
initAll,
Header
}
File all.js is located in node_modules.
When I tried to import it directly from index.html like:
<script type="module" src="node_modules/#id-sk/frontend/govuk/all.js"></script>
It is not working. Console error, file not found.
I also tried import it via angular.json:
"scripts": [
"./node_modules/#id-sk/frontend/govuk/all.js"
]
Also not working with error "Uncaught SyntaxError: Cannot use import statement outside a module (at scripts.js:15241:1)". The error refers to line:
import { initExtended } from './extended'
I also tried to import it in polyfills but I don't know how to call it.
As you are speaking about angular.json, I assume that you are working in an Angular application bootstrapped using the Angular CLI with default settings.
To be able to use this package #id-sk/frontend in your typescript files, you have to import it directly into your typescript file.
1. Import #id-sk/frontend in your TS files
// Import everything into a local variable
import * as govuk from '#id-sk/frontend';
// Import specific object
import { HeaderExtended } from '#id-sk/frontend';
2. Run ng serve
⚠ Spoil: It will lead to typings errors
3. Let's add or create typings
As #id-sk/frontend is not written in typescript, the compile doesn't know about the typings of this library.
Following this statement, you have two choices:
Find or contribute to DefinitelyTyped in order to create the typings of your package #id-sk/frontend
Create a local file typings.d.ts in your ./src folder to declare an empty module
declare module "#id-sk/frontend"
4. Kill & run ng serve again
Enjoy it!
Go further
You can add typings to your module in order to give you autocompletion on the provided objects of #id-sk/frontend.
``ts
declare module "#id-sk/frontend" {
export interface Options {
scope?: Document
}
export function initAll(options: Options): void;
}
Related
Current situation (simplified):
type OneCtrl = import("some-package").default.OneCtrl
type TwoCtrl = import("some-package").default.TwoCtrl
declare module "my-package" {
export function useUtils(options: OneCtrl): TwoCtrl
}
I am having to repeatedly import individual types from the default import.
Using <reference> didn't work.
I tried type g = import("some-package").default and then using g.OneCtrl but that didn't work either.
I am importing from a package that has a namespace. Direct ES6 imports would result in the definition file not being an ambient definition anymore which is what I need otherwise the project gives errors.
Environment (For Context): Vite + Vue 3 + TS Project
Update on specifics
I am trying to add types to this external package vue-grapesjs-composables which is based on the original grapesjs package which does have types.
What I mean by having tried ES6 imports is that this would work in the definition file:
import type grapes from 'grapesjs'
declare module "vue-grapesjs-composables" {
export function useGrapes(editorConfig: grapes.EditorConfig): grapes.Editor;
}
but gives this error when I import it in a component:
Could not find a declaration file for module 'vue-grapesjs-composables'
EDIT: Unfortunately this seems like a known issue that cannot be solved without messing with create-react-app, although I could be wrong :( https://github.com/facebook/create-react-app/issues/3547#issuecomment-593764097
I am working on a react project using typescript and firebase functions, with my firebase functions project folder inside the project folder for the react app. As there are lots of enums and interfaces that I want to keep consistent between my frontend (the react app) and my backend (the firebase functions), I use a symlink to share a folder containing files common between these two projects. This works fine with interfaces, but causes errors when I try to use an enum exported from this symlinked folder:
ERROR in ./functions/src/shared-types/roles.ts 3:0
Module parse failed: The keyword 'enum' is reserved (3:0)
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/source-map-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
| __webpack_require__.$Refresh$.runtime = require('/Users/joel/my-project/node_modules/react-refresh/runtime.js');
|
> enum Roles {
| Admin = 'Admin',
| Access = 'Access',
Repro
Starting from a fresh create-react-app with typescript support, add a folder called shared-types at the same level as src and put a file in it called MyEnum.ts:
// shared-types/MyEnum.ts
enum MyEnum {
Foo = "foo",
Bar = "bar"
}
export default MyEnum;
Then, make a symlink between that folder and another one also called shared-types inside src:
$ ln -s /path/to/project/shared-types /path/to/project/src/shared-types
Then, import and use MyEnum in App.tsx:
// src/App.tsx
import React from 'react';
import './App.css';
import MyEnum from "./shared-types/MyEnum";
function App() {
return (
<div className="App">
<h1>{MyEnum.Bar}</h1>
</div>
);
}
export default App;
Finally, just run npm start:
ERROR in ./shared-types/MyEnum.ts 3:0
Module parse failed: The keyword 'enum' is reserved (3:0)
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/source-map-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
| __webpack_require__.$Refresh$.runtime = require('/Users/joel/repro/node_modules/react-refresh/runtime.js');
|
> enum MyEnum {
| Foo = 'foo',
| Bar = 'bar',
Things that aren't causing it
It's not that typescript is ignoring the shared-types folder, as everything compiles fine if you add an interface to that folder and use it in App.tsx. Plus, running tsc --listFilesOnly will return a list including /path/to/project/src/shared-types/MyEnum.tsc.
It's not that my version of typescript doesn't support enums or that enums are disabled, as everything works fine if you add an enum to App.tsx itself.
Thanks in advance for the help! And feel free to suggest better ways of sharing files between these two projects in the comments if there are any!
It turns out that create-react-app doesn't support symlinks under src at all! The fact that the interface stuff worked at all seems to be a fluke.
https://github.com/facebook/create-react-app/issues/3547
This seems to have been a known issue for four years, but there is no blessed solution to it yet. 🫤
Suppose I have created an npm module named #myscope/blurfl that contains a couple of classes: A, defined in A.js and B defined in B.js, that are both re-exported through blurfl/index.js:
#myscope/
blurfl/
A.js
B.js
index.js
A.js:
export class A {...}
B.js:
import { A } from './A.js';
export class B {...}
index.js:
export * from "./A.js";
export * from "./B.js";
I would prefer to use import { A } from '#myscope/blurfl' instead of import {A} from './A.js' to keep the code cleaner (and make it easier to move an export into a different file), but #myscope/blurfl is obviously not a dependency of #myscope/blurfl itself, so the node module resolver can't find it if I run node index.js to check for missing dependencies.
Is there any way to import another item co-exported from the same index.js file without using the item's explicit filename?
I'm assuming you are using a current version of Node.js (12 LTS or later).
Make sure that the "name" of your package in package.json is really "#myscope/blurfl" (and not just "blurfl").
Now, add an "exports" section to the package.json specifying the relative path of the main export, i.e.
"exports": {
".": "./index.js"
}
You should now be able to use import { A } from '#myscope/blurfl' from within your package, too.
So, to summarize:
Use Node 12 or later.
Check the "name" in package.json.
Add an "exports" section to package.json indicating the main entry point export.
I am trying to import a module from a file outside my /cypress directory into the /cypress/integration directory's test.spec.js file like so:
import { LAB_MODEL } from '../../models/search/lab-model';
But inside this imported module "LAB_MODEL" there are other files being imported using the "##" at the start of the file imports like
import ExperimentDetails from "##/components/experiment/ExperimentDetails";
and I think this is why Cypress isn't working and giving me this error:
Error: Webpack Compilation Error
./models/search/lab-model.js
Module not found: Error: Can't resolve '##/components/experiment/ExperimentDetails' in '/Users/hidden/models/search'
resolve '##/components/experiment/ExperimentDetails' in '/Users/hidden/models/search'
Parsed request is a module
using description file: /Users/hidden/package.json (relative path: ./models/search)
Field 'browser' doesn't contain a valid alias configuration
resolve as module
So I think this is the reason why my test won't run, but I have no idea how to make Cypress recognize "##" imports and can't find any documentation/stackoverflow answers, any help is appreciated, thanks!
##/ looks like something that gets translated into a full path during the Nuxt build.
(ref The alias Property).
Cypress has a separate build that doesn't know about this Nuxt feature. You could try to replicate it with some webpack config via a preprocessor, but another way is to have your Nuxt app put a reference to lab_model on the window object
// somewhere in the Nuxt app
if (window.Cypress) {
window.lab_model = LAB_MODEL;
}
then at the top of the test
const lab_model = cy.state('window').lab_model;
This has the benefit of giving you the exact same instance of lab_model, in case you wanted to stub something.
In a starter Nuxt app, I added the code window.lab_model = LAB_MODEL in /pages/index.vue, but you can add it in any component that imports it, right after the import statement.
In the spec add a .should() to test the property exists, to allow the app time to settle.
it('gets labModel from the Nuxt app', () => {
cy.visit('http://localhost:3000/')
cy.window()
.should('have.property', 'lab_model') // retries until property appears
.then(labModel => {
console.log(labModel)
// test with labModel here
})
})
I have a TypeScript app using both .ts and .d.ts files. References are made using the triple slash notation /// <reference path=...>. My app has the following definition file:
declare module APP {
class Bootstrap {
constructor();
}
}
I then declare a module named "app" so I can import it in other files:
declare module "app" {
export = APP;
}
Assuming my Bootstrap class exists, I then import the Bootstrap class using:
import { Bootstrap } from 'app';
This works.
Now I create a submodule on APP, like so:
declare module APP.Service {
class Async {
constructor();
}
}
I make another declaration for the submodule:
declare module "app/service" {
export = APP.Service;
}
Now, when I import the class Async like so:
import { Async } from 'app/service';
I get the following error message:
Module '"app/service"' resolves to a non-module entity and cannot be imported using this construct.``
How do I be import a class from a submodule?
NOTE
I have found a workaround by declaring a global var:
declare var APP_SERVICE: APP.Service.IService; // IService exists
And exporting that on my module:
declare module "app/service" {
export = APP_SERVICE;
}
The downside of this is that my global namespace get's polluted with var's I don't use, because I will use Service through App.Service, not APP_SERVICE.
If you care about creating a nice, reusable, maintainable declaration file for a module, the preferred way is by using typings, as it handles aspects such as type dependency management and submodules very well for you.
Create a separate repository for the typings your are writing.
Add typings.json file with the name and path to your main typings. eg:
{
"name": "my-main-module",
"main": "index.d.ts"
}
Add your the type declarations for your main module to index.d.ts and the type declarations for submodules to submodule-name.d.ts.
submodule.d.ts
export interface submoduleInterface {
someProp: number
}
index.d.ts
import * as submodule from './submodule'
export interface mainInterface {
someProp: number
}
Run typings bundle index.d.ts -o bundle.d.ts. This will bundle all your typings into one type declaration file, declaring the proper submodules and respecting all the necessary dependencies between modules, submodules and even external modules.
Either copy this file to a custom-typings directory in your original project, or submit this repo to the typings registry so other people can also profit from it 🙂, and pull it in with a normal typings i my-module-name.
Here is the gist with all the code, and the resulting bundle.d.ts. Here is a repo that uses both submodules (redux-persist/constants) as external dependencies (redux), and that got ultimately submitted to the typings registry.