Automatically generating ambient module declarations - module

Given these 2 typescript files
api/Token.ts
interface Token {
code: string
}
export default Token
and index.ts
export * from './api/Token'
tsc 1.5 with the --declarations switch will generate two .d.ts files (with similar content)
api/Token.d.ts
interface Token {
code: string;
}
export default Token;
and index.d.ts
export * from './api/Token';
Running grunt-dts-bundle with the following options
dts_bundle: {
release: {
options: {
name: 'my-module',
main: 'index.d.ts'
}
}
}
will generate an ambient module declaration file my-module.d.ts with the following content
declare module 'my-module' {
export * from './api/Token';
}
However this declaration does not compile due to : Import or export declaration in an ambient module declaration cannot reference module through relative module name.
How can I automatically generate an ambient module declaration for the two typescript files above ?
EDIT
Please follow latest updates on https://github.com/Microsoft/TypeScript/issues/2262

I've recently written a blog post about this. To summarize, you can use autodts if you replace index.ts with api.ts, containing the following:
export {default as Token} from './api/Token';
Make sure api.ts is in the same place as the api directory (next to, not inside it).
Then you need a package.json file:
{
"name": "api",
"version": "1.0.0",
"main": "dist/api.js",
"scripts": {
"preinstall": "npm install autodts",
"postinstall": "autodts link",
"prepublish": "tsc && autodts generate"
},
"typescript": {
"definition": "index.d.ts"
},
"dependencies": {
"autodts": "~0.0.4"
},
"devDependencies": {
"#lib/autodts-generator": "~0.0.1",
"typescript": "~1.5.3"
}
}
It's important that the package name api matches the file api.ts and directory api. This way both Node.js and the TypeScript compiler will look in the same places when using your package.
Finally, you need a tsconfig.json file:
{
"compilerOptions": {
"declaration": true,
"module": "CommonJS",
"target": "es5",
"outDir": "dist"
},
"files": [
"api.ts"
]
}
Now npm install will compile your package and produce a bundled index.d.ts file as defined in the definition setting in package.json.
To use your package, you can do something like:
/// <reference path = "api/index.d.ts" />
import {Token} from 'api';
class foo {
key: Token;
}
You can use autodts link to keep the reference path up to date, check the blog post and/or autodts docs for that.
The resulting index.d.ts contains:
/// <reference path="index.ref.d.ts" />
declare module 'api/Token' {
interface Token {
code: string;
}
export default Token;
}
declare module 'api' {
export { default as Token } from 'api/Token';
}
index.ref.d.ts and api.js are blank.

Related

Uncaught Error: 'target' is a required option - Svelte

I'm building an NPM package for Svelte. With this package I export a couple of simple components:
import SLink from './SLink.svelte';
import SView from './SView.svelte';
export { SLink, SView };
This is before bundling them to a minified version using rollup.
Rollup config:
module.exports = {
input: 'src/router/index.ts',
output: {
file: pkg.main,
format: 'umd',
name: 'Router',
sourcemap: true,
},
plugins: [
svelte({
format: 'umd',
preprocess: sveltePreprocess(),
}),
resolve(),
typescript(),
terser(),
],
};
package.json (minus unnecessary info):
{
"name": "svelte-dk-router",
"version": "0.1.29",
"main": "dist/router.umd.min.js",
"scripts": {
"lib": "rollup -c lib.config.js",
},
"peerDependencies": {
"svelte": "^3.0.0"
},
"files": [
"dist/router.umd.min.js"
],
}
When I publish the package and test it, I get this error:
Uncaught Error: 'target' is a required option
at new SvelteComponentDev (index.mjs:1642)
at new Home (App.svelte:5)
at Z (router.umd.min.js:1)
at N (router.umd.min.js:1)
at new t.SView (router.umd.min.js:1)
at create_fragment (App.svelte:5)
at init (index.mjs:1476)
at new App (App.svelte:5)
at main.js:7
at main.js:9
Which seems to be something to do with mounting the component as target is used to mount:
const app = new App({ target: document.body })
The odd thing is, SLink on it's own works fine, mounts as it should etc., it's just SView that doesn't work.
SLink:
<script lang="ts">
import { writableRoute, changeRoute } from '../logic';
export let name: string = undefined,
path: string = undefined,
query: Record<string, string> = undefined,
params: Record<string, string> = undefined;
let routerActive: boolean;
writableRoute.subscribe(newRoute => {
if (newRoute.path === '*') return;
const matches = (path && path.match(newRoute.regex)) || newRoute.name === name;
routerActive = matches ? true : false;
});
</script>
<div
on:click={() => changeRoute({ name, path, query, params })}
class={routerActive ? 'router-active' : ''}>
<slot />
</div>
SView:
<script lang="ts">
import { writableRoute } from '../logic';
let component: any;
writableRoute.subscribe(newRoute => (component = newRoute ? newRoute.component : null));
</script>
<svelte:component this={component} />
I've tried the components uncompiled, as per these docs, but then the imports don't work.
Anyone have any idea how I can work around this issue?
I recently ran into this problem. I was setting up a new page and forgot the defer keyword on the script. I had:
<script src="/app/public/build/bundle.js"></script>
and it needed to be
<script defer src="/app/public/build/bundle.js"></script>
Notice the missing defer.
All components in a Svelte tree need to be compiled with the same Svelte compiler (i.e. same node_modules/svelte) all at once. This includes components from external libs like what you are trying to achieve. This is what is explained in the docs you've linked.
The main field of the package.json will expose the compiled components, for usage in a non Svelte app.
You also need a svelte field to expose the uncompiled version of the components, for consumption by a Svelte app. You can still export multiple Svelte components from a .js file.
package.json
{
"name": "svelte-dk-router",
"version": "0.1.29",
"main": "dist/router.umd.min.js",
"svelte": "src/index.js",
"scripts": {
"lib": "rollup -c lib.config.js",
},
"peerDependencies": {
"svelte": "^3.0.0"
},
"files": [
"dist/router.umd.min.js"
],
}
src/index.js
import SLink from './SLink.svelte';
import SView from './SView.svelte';
export { SLink, SView };
rollup-plugin-svelte will pick up the svelte field in your lib's package.json, so that import of Svelte components resolve correctly.
I don't know about your issue but I was getting the same error message for this reason:
In the template.html file located in the src folder, I removed the element that has the #sapper ID and replaced the %sapper.html% code inside the body tag. The next thing to do was to change the render target to the document.body, in the client.js file located in the src folder. I forgot to do this step and this was causing that error message.

How to create my own component library based on Vuetify

I want create my component library based on Vuetify and publish on npm.
Vuetify has already vue-plugin standard installation and use vuetify-loader, I think was a more complex scenario than plain HTML component.
For example, I want create my Login Form, my Article Page, my default calendar picker with preset values.
There's a standard way or guide to do this or a sample to do that?
I use last version of vuetify (2.0.7)
I just got it working for Vue3/Vuetify3. In a nutshell (using pnpm, vite, typescript, Vue plugin):
Create the component as a new project:
pnpm create vue#latest
-> your-plugin
-> Typescript
-> ESLint
cd <project>
echo auto-install-peers = true > .npmrc
pnpm add -D vuetify rollup-plugin-typescript2
Then remove all the components and make your own component instead.
Create src\components\index.ts and src\YourPlugin.ts
src\components\index.ts
export {default as YourComponent} from "./YourComponent.vue"
src\YourPlugin.ts
import type { App } from "vue"
import { YourComponent } from "./components"
export default {
install: (app: App) => {
app.component("YourComponent", YourComponent)
}
};
vite.config.ts
Add to the imports:
import vuetify from 'vite-plugin-vuetify'
import typeScript2 from "rollup-plugin-typescript2"
Add to the plugins:
vuetify({
autoImport: true,
}),
typeScript2({
check: false,
include: ["src/components/*.vue"],
tsconfigOverride: {
compilerOptions: {
sourceMap: true,
declaration: true,
declarationMap: true,
}
},
exclude: [
"vite.config.ts"
]
})
Add a new section build to the defineConfig:
build: {
cssCodeSplit: false,
lib: {
entry: "./src/YourPlugin.ts",
formats: ["es", "cjs"],
name: "CommonVtfyPlugin",
fileName: format => (format == "es" ? "index.js" : "index.cjs"),
},
rollupOptions: {
external: ["vue", "vuetify/lib"],
output: {
globals: {
vue: "Vue",
vuetify: "Vuetify",
'vuetify/components': 'VuetifyComponents',
'vuetify/directives': 'VuetifyDirectives'
}
}
}
},
dist\index.d.ts
I have not figured out how to generate this on yet. But this is a generic stand in:
declare const _default: any;
export default _default;
package.json
Add this:
"type": "module",
"exports": {
".": "./dist/index.js"
},
"types": "./dist/index.d.ts",
"files": [
"dist"
],
You can use it in any Vue project by importing it as a plugin:
import YourComponent from 'your-plugin'
app.use(YourComponent)
No guarantees on how optimized that is (feedback welcome).. but it works (tm)..
A more detailed answer can be found pnpm monorepo: how to set up a simple reusable (vue) component for reuse? (any updates will primarily be updated in that answer as well, if any)

Cannot use Vue component library with npm link

I am building a Vue application and a Vue component library at the same time. So, I would like to setup the library with npm link so I don't have to keep publishing my library package and re-install it in my main application.
My package will be called #company/vue. I can publish it to npm and install/use it in my Vue app like this:
import Vue from 'vue';
import VueComponents from '#company/vue';
Vue.use(VueComponents);
And that works fine. I can access the components in my .vue files and all that.
However, if I follow the standard steps to linking my library:
cd path/to/library
npm link
cd path/to/application
npm link #company/vue
Upon starting dev mode, I get this warning:
export 'default' (imported as 'VueComponents') was not found in '#company/vue'
And of course, nothing in the page loads.
I have to imagine that I'm possibly bundling it wrong?
My build script looks like this:
vue-cli-service build --no-clean --target lib --name company-vue src/index.js
And my index.js that it refers to:
import './index.scss';
import * as components from './components/index.js';
const CompanyVue = {
install(Vue) {
if (this.installed) {
return;
}
this.installed = true;
for (let name in components) {
Vue.use(components[name]);
}
}
};
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
}
else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(Plugin);
}
export * from './components';
export default CompanyVue;
This is just the standard way I've seen most libraries do it. And again, it works fine if I pull the package from npm.
This is the stuff related to my bundling in my package.json:
"files": [
"dist",
"src",
"!src/**/*.spec.js",
"!src/**/*.stories.js"
],
"main": "./dist/company-vue.common.min.js",
"module": "./dist/company-vue.umd.min.js",
"browser": "./dist/company-vue.umd.min.js",
"unpkg": "./dist/company-vue.umd.min.js"
And finally, my babel config for the component library:
module.exports = {
presets: ['#babel/preset-env'],
env: {
test: {
presets: [[
'#babel/preset-env',
{
targets: { node: "current" }
}
]]
}
}
}
i found a solution in this issue:
just add this to your vue.config.js in your project:
configureWebpack: {
resolve: {
symlinks:false //npm link
},
}

How to use symlinks in React Native projet?

The symlink support is still not officially available in react-native https://github.com/facebook/metro/issues/1.
It's actually possible to use symlinks in the package.json with npm (not yarn)
{
"name": "PROJECT",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"my_module1": "file:../shared/my_module1/",
"my_module2": "file:../shared/my_module2/",
"react": "16.8.3",
"react-native": "0.59.5",
},
"devDependencies": {
"babel-jest": "24.7.1",
"jest": "24.7.1",
"metro-react-native-babel-preset": "0.53.1",
"react-test-renderer": "16.8.3"
},
"jest": {
"preset": "react-native"
}
}
Although we will get my_module1 does not exist in the Haste module map
To fix this we could do before a metro.config.js (formerly rn-cli.config.js)
const path = require("path")
const extraNodeModules = {
/* to give access to react-native-firebase for a shared module for example */
"react-native-firebase": path.resolve(__dirname, "node_modules/react-native-firebase"),
}
const watchFolders = [
path.resolve(__dirname, "node_modules/my_module1"),
path.resolve(__dirname, "node_modules/my_module2"),
]
module.exports = {
resolver: {
extraNodeModules
},
watchFolders,
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false
}
})
}
}
Unfortunately it doesn't work anymore on react-native 0.59 The app is reloading, but changes in the source code are not reflected in the app. Anyone has a clue to achieve this?
I had a similar issue and found haul.
Follow haul getting started instructions.
Add your local dependencies with file:../ e.g:
// package.json
"dependencies": {
"name-of-your-local-dependency": "file:../"
}
reinstall node_modules with yarn --force
Start the development server by yarn start (haul will replace your start script)
Run react-native run-android or/and react-native run-ios
None of the answers I found worked for me and it seems symlinks are not going to be supported anytime soon (see: https://github.com/facebook/metro/issues/1), so I had to do it manually.
I am using onchange npm package and running that in my local package: onchange 'dist/**/*.js' -- cp -R ./dist ../../app/node_modules/#author/packagename
That worked for me to achieve development and testing, while not breaking anything for production releases. I hope this can save my peers a few headaches.
After a few years working on this, we found a reliable way using yarn.
Add your local dependencies with are inside a folder file:../CompanyPackages/package e.g:
// package.json
"dependencies": {
"local-package": "file:../CompanyPackages/local-package"
}
Use a custom metro.config.js
const path = require("path")
const { mapValues } = require("lodash")
// Add there all the Company packages useful to this app
const CompanyPackagesRelative = {
"CompanyPackages": "../CompanyPackages",
}
const CompanyPackages = mapValues(CompanyPackagesRelative, (relativePath) =>
path.resolve(relativePath)
)
function createMetroConfiguration(projectPath) {
projectPath = path.resolve(projectPath)
const watchFolders = [...Object.values(CompanyPackages)]
const extraNodeModules = {
...CompanyPackages,
}
// Should fix error "Unable to resolve module #babel/runtime/helpers/interopRequireDefault"
// See https://github.com/facebook/metro/issues/7#issuecomment-508129053
// See https://dushyant37.medium.com/how-to-import-files-from-outside-of-root-directory-with-react-native-metro-bundler-18207a348427
const extraNodeModulesProxy = new Proxy(extraNodeModules, {
get: (target, name) => {
if (target[name]) {
return target[name]
} else {
return path.join(projectPath, `node_modules/${name}`)
}
},
})
return {
projectRoot: projectPath,
watchFolders,
resolver: {
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
extraNodeModules: extraNodeModulesProxy,
},
}
}
module.exports = createMetroConfiguration(__dirname)
You can use yalc. Yalc will simulate a package publish without actually publishing it.
Install it globally:
npm i -g yalc
In your local package:
yalc publish
In the application:
yalc add package-name && yarn
After you made some changes to the package, you can just run
yalc push
and it will automatically update every app that uses your local package
Could never get my own environment working using any other suggestions, but found a hack that works well (though not ideal) that can be easily set up in just a few lines of code and without changing your RN project configuration.
Use fs.watch for changes recursively in the directory where you're working on your library, and copy the updates over whenever there's been a change:
import fs from 'fs'
const srcDir = `./your-library-directory`
const destDir = `../your-destination-directory`
fs.watch("./src/", {recursive: true}, () => {
console.log('copying...')
fs.cp(srcDir, destDir, { overwrite: true, recursive: true }, function() {
console.log('copied')
})
})
Only two things are required.
1.yarn --force
2.yarn start
then select android for running option

Aurelia using featherjs dependency failing to properly import feathers-socketio

How do you import featherjs-socketio using the technique common in Aurelia projects. A previous question helped me past the second dependency, but the last one seems to have the same fate as the first without a similar workaround. Aurelia using featherjs depency failing to properly import
This is what I have:
in the build file aurelia.json
"dependencies": [
{
"name": "socket.io-client",
"path": "../node_modules/socket.io-client/dist/socket.io.min"
},
{
"name": "feathers-client",
"path": "../node_modules/feathers-client/dist",
"main": "feathers"
},
{
"name": "feathers-socketio",
"path": "../node_modules/feathers-socketio",
"main": "client"
},
"aurelia-binding",
In the app.js
import io from 'socket.io-client';
import feathers from 'feathers';
import socketio from 'feathers-socketio';
export class App {
constructor() {
this.message = 'Hello World!';
console.log("startup");
const socket = io('http://localhost:3030');
const app = feathers()
.configure(socketio(socket));
}
}
The error looks like this:
Starting 'readProjectConfiguration'...
Finished 'readProjectConfiguration'
Starting 'processMarkup'...
Starting 'processCSS'...
Starting 'configureEnvironment'...
Finished 'processCSS'
Finished 'processMarkup'
Finished 'configureEnvironment'
Starting 'buildJavaScript'...
Finished 'buildJavaScript'
Starting 'writeBundles'...
Tracing app...
{ uid: 8,
name: 'writeBundles',
branch: false,
error:
{ [Error: ENOENT: no such file or directory, open '/Users/steve/project/src/feathers-socket-commons/client.js']
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/Users/steve/project/src/feathers-socket-commons/client.js',
moduleTree: [ 'feathers-socketio/lib/client' ],
fileName: '/Users/steve/project/node_modules/feathers-socketio/lib/client.js' },
duration: [ 0, 281071327 ],
time: 1484922063672 }
Once it gets into processing the dependency it seems to be having path confusion looking for dependencies in featherjs-socketio. I asked a previous question about the feathers dependency which is resolved, but not sure what to do with this one.
feathers-socket-commons is listed in the node modules, but it seems to be looking in my project folder so that is why I'm assuming it is confused on path, or simply not a client library? Strange because the examples show using this node module, but examples also show using 'feathers' instead of 'feathers-client'. This is the client example given in feathers-socketio:
Client use
import io from 'socket.io-client';
import feathers from 'feathers/client';
import socketio from 'feathers-socketio/client';
const socket = io('http://path/to/api');
const app = feathers()
.configure(socketio(socket));
SOLUTION Thanks to the marked answer below.
aurelia.json changes to:
"dependencies": [
{
"name": "socket.io-client",
"path": "../node_modules/socket.io-client/dist/socket.io.min"
},
{
"name": "feathers-client",
"path": "../node_modules/feathers-client/dist",
"main": "feathers"
},
"aurelia-binding",
app.js becomes:
import io from 'socket.io-client';
import feathers from 'feathers-client';
export class App {
constructor() {
this.message = 'Hello World!';
console.log("startup");
const socket = io('http://localhost:3030');
const app = feathers().configure(feathers.socketio(socket));
}
}
you don't need to install feathers-socketio it is included in feathers-client among other plugins.
configure(socketio(socket))
becomes:
configure(feathers.socketio(socket))
according to the feathers-socketio