How to create an "external module" typescript definition file to include with an npm package? - npm

I recently added a typescript definition file for the open source redux-ui-router library, but I'm now getting errors like the following with Typescript 1.7.3:
error TS2656: Exported external package typings file
'C:/.../node_modules/redux-ui-router/index.d.ts' is
not a module. Please contact the package author to update the package
definition.
I am trying to import this library with code like this in my typescript files:
import ngReduxUiRouter from "redux-ui-router";
I'm new to Typescript, and I can't find a clear description of what exactly this definition file should look like when included with an npm package. There's a wiki entry that talks about typings for npm packages, but outside of repeating the direction that an external module should be used, there's not a concrete example to work from.
CORRECTION
I've tried removing the declare module "redux-ui-router" { code, and that seemed to work after restarting webpack, which I'm using to compile everything (I removed the comments for brevity):
export interface ReduxUIRouterAction {
type: string;
payload: any;
}
export interface ReduxUIRouterState {
currentState: Object;
currentParams: Object;
prevState: Object;
prevParams: Object;
}
export function router(state: ReduxUIRouterState, action: ReduxUIRouterAction): ReduxUIRouterState;
export var ngReduxUiRouter: string;
export function stateGo(to: string, params?: Object, options?: Object): ReduxUIRouterAction;
export function stateReload(state: any): ReduxUIRouterAction;
export function stateTransitionTo(to: string, params?: Object, options?: Object): ReduxUIRouterAction;
export default ngReduxUiRouter;
Is this set of changes what would be expected when including this in an npm package?

Is this set of changes what would be expected when including this in an npm package?
Yes. The exports need to be root level at the file.
In other words : an ambient file is not an external module

Related

How to bundle tailwind css inside a Vue Component Package

In one of my projects, I build a nice vue3 component that could be useful to several other projects. So I decided to publish it as an NPM package and share it with everyone.
I wrote the isolate component, build it and publish BUT I use Tailwind css to make the style.
When I publish and install the component everything is working BUT without the beauty of the css part.
I tried several configurations and alternative tools to generate the package that automatically add the tailwind as an inner dependency to my package.
Does someone have experience with this? how can build/bundle my component by adding the tailwind CSS instructions into it?
You're almost there
Since you've got your component working, the majority of the part has been done.
For configuring the styling of the component you need to identify the Tailwind CSS classes being used by your Vue component package and retain them in the final CSS that is generated by the Tailwind engine in your project.
Follow below steps in the project where you want to use your tailwind vue component package.
For Tailwind CSS V3
// tailwind.config.js
module.exports = [
//...
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/package-name/**/*.{vue,js,ts,jsx,tsx}" // Add this line
// Replace "package-name" with the name of the dependency package
],
//...
]
For Tailwind CSS V2
// tailwind.config.js
module.exports = [
//...
purge: {
//...
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/package-name/**/*.{vue,js,ts,jsx,tsx}" // Add this line
// Replace "package-name" with the name of the dependency package
],
//...
//...
}
]
The content property in the tailwind.config.js file defines file path pattern that the tailwind engine should look into, for generating the final CSS file.
For Pro users
You may also try to automate the above setup by writing an install script for your npm package to add this configuration to the tailwind.config.js file
References
Tailwind Docs - 3rd party integration
It's a bit difficult for someone to answer your question as you've not really shared the source code, but thankfully (and a bit incorrectly), you've published the src directory to npm.
The core issue here is that when you're building a component library, you are running npm run build:npm which translates to vue-cli-service build --target lib --name getjvNumPad src/index.js.
The index.js reads as follows:
import component from './components/numeric-pad.vue'
// Declare install function executed by Vue.use()
export function install (Vue) {
if (install.installed) return
install.installed = true
Vue.component('getjv-num-pad', component)
}
// Create module definition for Vue.use()
const plugin = {
install
}
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null
if (typeof window !== 'undefined') {
GlobalVue = window.Vue
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue
}
if (GlobalVue) {
GlobalVue.use(plugin)
}
// To allow use as module (npm/webpack/etc.) export component
export default component
There is no mention of importing any CSS, hence no CSS included in the built version.
The simplest solution would be to include the index.css import in your index.js or the src/components/numeric-pad.vue file under the <style> section.
Lastly, I'm a bit rusty on how components are built, but you might find that Vue outputs the CSS as a separate file. In that case, you would also need to update your package.json to include an exports field.

How to force .vue extension in all imports using eslint?

In VS Code with Vetur (the extension for working with Vue), "Go to definition" will not work on component imports where there's no .vue extension at the end (Vetur FAQ link)
I was wondering if there's an eslint rule that will force the user to always provide an extension when using an import statement in .vue files?
Examples:
✔️ This works:
import HelloWorld from '#/components/HelloWorld.vue'
Right clicking on HelloWorld and pressing Go to definition in VS Code wil take you to the HelloWorld.vue file.
❌ This doesn't:
import HelloWorld from '#/components/HelloWorld'
If you press Go to definition on HelloWorld (leftmost), VS Code will just move the cursor to the HelloWorld you just right clicked. Intended behavior is that we move to the HelloWorld.vue file.
It's easy to do this for paths like ./src/components/A.vue. It's trickier for #/components/A.vue because you need to resolve the # alias.
The below solution works for both.
To force .vue extensions in paths, do this:
1. Install eslint-plugin-import, which extends functionality of eslint by linting import paths. Also install with a custom path resolver - eslint-import-resolver-alias for it:
npm install eslint-plugin-import eslint-import-resolver-alias --save-dev
2. Then, in your ESLint config file (.eslintrc.js, or eslintConfig field in package.json etc), add this:
// I'm using .eslintrc.js
module.exports = {
//...unimportant properties like root, env, extends, parserOptions etc
plugins: ["import"],
settings: {
"import/resolver": {
alias: {
map: [
["#", "./src"], //default # -> ./src alias in Vue, it exists even if vue.config.js is not present
/*
*... add your own webpack aliases if you have them in vue.config.js/other webpack config file
* if you forget to add them, eslint-plugin-import will not throw linting error in .vue imports that contain the webpack alias you forgot to add
*/
],
extensions: [".vue", ".json", ".js"]
}
}
}
}
Here's a repository with a working demo that implements forcing .vue path in imports correctly.
And a screenshot from VSCode and output from npm run lint:
You need to configure eslint-plugin-import to set force on vue files, just add this rule in eslint config
"import/extensions": ["error", "ignorePackages", { "vue": "always" }],

Intellisense with 3rd part libraries and angular-cli

In a project created with angular2-cli I need to use some 3rd part javascript libraries that should be available in the global javascript scope (for example TWEEN namespace from the tween.js library).
Accordingly to the angular-cli guide to install a js library as global I've installed it with npm and add the library script in the "scripts" array of the angular-cli.json file.
"scripts": [
"../node_modules/tween.js/src/Tween.js"
],
To use the global TWEEN namespace in an angular component I've declared it as a constant variable at the beginning of the file, like the following:
import { Component, OnInit } from '#angular/core';
declare const TWEEN: any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
title = 'app works!';
constructor() { }
ngOnInit() {
let tween = new TWEEN.Tween({x: 0}).to({x: 1}, 2000);
// ..
}
}
This works, but the problem is that I am not getting any intellisense for any of the 3rd part libraries in this way (I am using WebStorm). Is there anything I can do to get intellisense working? Or are there better ways to import 3rd part javascript libraries in an angular 2 workflow?
You are never going to get intellisense this way, given that you are defining TWEEN at the top of your file as "any", hence saying it could be anything (no type, no intellisense).
You should take a look at this post:
https://weblog.west-wind.com/posts/2016/Sep/12/External-JavaScript-dependencies-in-Typescript-and-Angular-2
It's pretty much telling you that you either have typings in the library itself, which come for free when you install it, or just work around not having the typings.
If you are going to be working with libraries that have no typings at all, you might want to consider creating the typings yourself.

Generate PDF file from html using angular2/typescript

I want to take a part of my HTML template and convert it to PDF file to give the user an option to download it. (After they click a button for example).
I found a library called jsPDF, how would I use jsPDF in an Angular2 app (RC4)?
thank you
If you want to use it in production, you definitely don't want to depend on an internet link being referenced in your index.html, like proposed by #khalil_diouri.
So, to properly use it in an Angular2/Typescript environment,
first install it from npm
npm install --save jspdf
If you are using SystemJS, map it in your config file
map: {
"jspdf": "node_modules/jspdf/dist/jspdf.min.js"
}
Install definition package: (if not installed)
npm install typings --global
Install definition files:
typings install dt~jspdf --global --save
And, finally, import it in your component.ts file
import jsPDF from 'jspdf'
...
let doc = new jsPDF();
doc.text(20,20,'Hello world');
doc.save('Test.pdf');
...
as a response
this link is necessary to import jsPDF content
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"></script> // to use jsPDF for registring pdf file
then in you component.ts
you do that
declare let jsPDF;
#Component({
template: `
<button
(click)="download()">download
</button>
`
})
export class DocSection {
constructor() {
}
public download() {
var doc = new jsPDF();
doc.text(20, 20, 'Hello world!');
doc.text(20, 30, 'This is client-side Javascript, pumping out a PDF.');
doc.addPage();
doc.text(20, 20, 'Do you like that?');
// Save the PDF
doc.save('Test.pdf');
}
}
the AddHtml method is deprecated :
Source:
plugins/addhtml.js, line 12
Deprecated:
This is being replace with a vector-supporting API. see here
Renders an HTML element to canvas object which added to the PDF
This feature requires html2canvas or rasterizeHTML
Why use Definition (also known as Declaration) files?
To use external javascript libraries (jsPDF, for example) with Angular2 applications (which use Typescript) you are going to want Type Definition files for those javascript libraries. These files provide type information (as in String, Number, boolean, etc.) to typescript for help with compile time type checking. (Since javascript is loosely typed)
Another explanation about d.ts files can be found here.
How to use
You can download an npm package called typings which will help expedite the process. Here's a short guide on how to use it. Once you have typings installed, you can run:
npm run -- typings install dt~jspdf --global --save
to get the typings file which you can then use in your project.

Using the Material Design package for Angular2

According to the angular/material2 site, component packages can be installed with commands like npm install #angular2-material/checkbox.
When I used this command, the #angular2-material/checkbox folder doesn't have a checkbox.ts file for the checkbox element. Instead, it has a checkbox.d.ts file that declares a regular class named MdCheckbox. This isn't a component, so I can't access it in my Angular2 template.
When I download the full angular/material2 archive, the src folder contains checkbox.ts, which defines MdCheckbox as a component. So is there a problem with the npm package or the GitHub archive?
In short, no there is no problems in it. The checkbox.d.ts file isn't a component file. It's just a declaration file. The actual file that has the component is checkbox.js, it's already been transpiled to javascript.
In order to use it, "assuming you have SystemJS as your module loader, which is the default". In your index.html
System.config({
map:{
'#angular2-material/checkbox':'node_modules/#angular2-material/checkbox'
},
packages: {
'#angular2-material/checkbox': {
format: 'cjs',
defaultExtension: 'js',
main: 'checkbox.js'
},
app:{...}
}
});
And then, in your component:
import {MdCheckbox} from '#angular2-material/checkbox';