I'm working with React Native and using index.js to manage modules. I have many projects consuming from the same components folder, which has a structure like this:
components
|_ComponentOne.js
|_ComponentTwo.js
|_index.js
In which the index.js looks like this:
export * from './ComponentOne.js';
export * from './ComponentTwo.js';
Now lets say I have three projects:
ProjectOne, which uses ComponentOne;
ProjectTwo, which uses ComponentTwo;
ProjectThree, which uses both;
Every project has its own files, but they refer to this same folder to use components (like a shared assets folder). Everything works fine while all the projects have all dependencies for all components.
In another words, I have a problem when one of the projects doesn't install a dependency for one of the components, even when the project doesn't uses that component.
Let's take as an example ProjectOne, which uses only ComponentOne. If ComponentTwo (which is not used in this project) has dependency X, I have to npm install dependency X even on ProjectOne, or an error is given. Again, ProjectOne doesn't use dependency X.
I can only image this happens because the index.js validates all declared exports, even when they're not used.
I'm trying to find an alternative to not be forced to install plugins and other things that I won't even use in my projects. I know that if I remove the index.js and start importing files directly on projects, it will work, but I would like to keep the index.js structure (to be able to use multi import syntax import { ComponentOne, ComponentTwo } from 'components').
Any suggestion?
Update:
The error I get, when I do not npm install dependency X is
Module `X` does not exist in the Haste module map
If I install it, everything works.
I'm using the terminal to install the application directly into an android phone. The JS bundle is automatically created by Metro (RN default).
Related
I've a npm package with the following three files in the dist folder (generated by webpack):
Inside the package.json file I've declated the sample.js file as the main one: "main": "dist/sample.js",.
Now I wan't to use this package in another project. Did I need to import all three files? Or should all work fine with one import like import aFunction from 'package-name'?
If there aren't any other weird dependencies, and sample.js is actually the entrypoint of the library (i.e. either you do everything inside it, or you make all the needed exports from it), it should work fine just as you wrote, i.e. import aFunction from 'package-name'.
Btw, is sample.js minified? As far as I remember, starting with Webpack v4 it does the minification automatically for production builds. But you might still want to doublecheck (read here for more info).
I am currently testing stencil js. For now I want to write stencil components and include them within a VUE/React project. The official website of stencil already shows how to integrate them within a framework (https://stenciljs.com/docs/overview). But they assume that your own stencil component library has already been published to npm.
Is there a way to integrate stencil components locally into a framework to test them without publishing them first?
Yes, you can use npm-link for that.
cd my-component-lib
npm link
cd ../my-app
npm link my-component-lib # or whatever you have named the project in package.json
If you have any problems with that (e. g. with paths not resolving properly), you can also try to pack your package and install the packed version instead, using npm-pack:
cd my-component-lib
npm pack
cd ../my-app
npm install ../my-component-lib/my-component-lib-1.0.0.tgz
Linking is preferable though because changes to your component library will be reflected immediately (after a rebuild), whereas with packing you'd have to re-pack and re-install it after every change to your lib.
Instead of publishing or packing your packages, you could utilize TypeScript's path mapping feature.
This allows you to write your import statements just as you would with a published package, but behind the scenes TypeScript maps the imports to their given source code location.
Here's an example of a tsconfig.json with path mapping added to the compiler options:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"ui-components": ["libs/ui-components"],
"ui-components/loader": ["libs/ui-components/dist/loader/index.cjs.js"],
"ui-components-react": ["generated/ui-components-react/src/components.ts"]
},
...
As you can see, it has 3 mappings: the path to the core Stencil components ui-components, the path to the generated React components which are exposed as ui-components-react, as well as the generated loader ui-components/loader which provides the bridge between the Custom elements and the React wrappers.
I created a full working example for Stencil Web Components with generated bindings and wrappers for React that comes without the need of publishing any package: Nx Stencil React.
Please note that this answer is based on #stencil/core 1.14.0 or below. Future versions may have a different approach on generating the framework integrations.
I've had quite a bit of trouble with this myself so will provide an answer specifically for Vue 3 as Stencil's Framework Integrations guide seems to refer only to Vue 2.
Starting Projects
Stencil Component
Following the Getting Started guide run npm init stencil. Choose the component option.
There was a bug in v2.7.0 so I update to v2.8.0 with npm i #stencil/core#latest --save-exact
Build the project with npm run build
Optional
By default, the stencil project configures multiple build targets, to make it easier to see what build files are being used you can edit the stencil config to only include the custom elements bundle:
\\ stencil.config.ts
outputTargets: [
{
type: 'dist-custom-elements-bundle',
},
{
type: 'dist',
esmLoaderPath: '../loader',
},
],
You also need the 'dist' type for the .d.ts typings file to be generated with your custom-elements (not sure why).
Vue 3 App
Using a globally installed Vue CLI #vue/cli#4.5.13 create a new Vue 3 default project.
Using Stencil in Vue 3
Install your stencil component project
npm install --save ../<path>/stencil-component as a dependency of your vue app.
Fixing NPM Module Resolution
Following the Vue CLI - Troubleshooting guide add a vue.config.js file to the root of your Vue 3 project with the line config.resolve.symlinks(false),
Skipping Component Resolution
In the same file we need to configure Using Custom Elements in View
\\ vue.config.js
module.exports = {
chainWebpack: (config) => {
config.resolve.symlinks(false),
config.module
.rule("vue")
.use("vue-loader")
.tap((options) => ({
...options,
compilerOptions: {
isCustomElement: (tag) => tag.includes("my-"),
},
}));
},
};
Framework Integration
Now we can declare the custom elements, but in the Vue 3 way
\\ main.js
import { createApp } from 'vue'
import App from './App.vue'
import { defineCustomElements } from "stencil-component";
defineCustomElements();
createApp(App).mount('#app');
You can now use your custom component as normal. Here's what my App.vue file looked like after hacking the example starter code:
<template>
<my-component first="Andy" middle="2K" last="11"></my-component>
</template>
<script>
import { MyComponent } from "stencil-component";
export default {
name: 'App',
components: {
MyComponent
}
}
</script>
Errors
No ESLint Config
No ESLint configuration found in /<path>/stencil-component/dist/custom-elements.
Fixed by telling webpack not to resolve symlinks in vue.config.js
Uncaught TypeError: class constructors must be invoked with 'new'
This error occurs in the browser after a successful compilation.
Resolved by telling webpack / vue not to resolve your custom components
Custom Component Not Visible
There are no errors and your component is showing in the DOM inspector but not appearing on the page.
You need to defineCustomElements() in main.js.
Component not found
I've had some variation of this error when trying to import and use my component but haven't been able to reproduce it just now. Doing all of the above and restarting the dev server works fine for me.
For local integration, you can reference the esm.js file inside www/build folder which can be used in the head tag of the Vue/React project.
For eg if you have the below 2 apps
stencil-components - stencil components
stencil-react - sample react app which will consume the components.
Once you run stencil-components by npm run start it will be hosted at 3333 (by default).
Including below line in head ofindex.html of stencil-react will integrate components with live reloading on change.
<script type="module" src="http://localhost:3333/build/stencil-components.esm.js"></script>
I have a few npm modules published, all modules for existing libraries, like three.js or react.
The packages seem to be downloaded but i've received no feedback on whether it's done right or not.
Dependencies
What is the high level goal when defining dependencies?
three.js:
This is confusing because every "extension" just assumes that there is a THREE object available in some context.
My three.js module thus only mentions:
"devDependencies": {
"three": "^0.88.0"
}
And it's being used like this:
require( 'three-instanced-mesh' )(THREE)
Which both makes sense and doesn't.
The module can't work without three.js and a proper context passed in (THREE), but since i pass it in at runtime(?) it doesn't seem like it is an actual dependency. When I checkout the repo and want to develop in it, i do need to install three.js if i want the code to run.
React
I've published a React component which i intended to be used as such:
npm install my-module
import MyModule from 'my-module
<MyModule/>
For some reason I listed react as a peerDependencies dep.
<MyModule/> in JSX would imply that I've done something to have react already available in this context (similar to how THREE is passed in first example?).
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
What is the desired goal here and how to describe it? I only know that i don't want npm install my-module to install a different version of react, or to cause more react somehow to be bundled in the final bundle (but i'm not even sure about that).
What type of dependency (if any) should react be to my-react-component and how would i actually link it to my module?
For example using the externals thing with webpack vs having an actual import React from 'react'?
Build
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
What is the high level goal when defining dependencies?
You have to define which dependencies you are using only when developing (devDependencies) and the ones that are needed when someone installs your package and are going to be installed automatic (dependencies), and dependencies you need to be available, but you want the user to install (which, honestly, does not makes sense) peerDependencies.
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
It would require React to be available where the file is being imported, i.e: A imports myModule, but A has to have react imported. Putting as peer dependencies is the best way here indeed.
For example using the externals thing with webpack vs having an actual import React from 'react'?
Using externals in webpack just tells webpack to not bundle react and says that react will have been imported before the import of this component.
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
Usually the index.js file that contains the library minified/bundled. Publish that with npm, you'll need to setup main field on package.json
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
foo points to the name of the package that you created, i.e: the name this package was publish under. When you go to npmjs.org and search for foo, that is going to be the package. foo is in your node_modules.
I'm converting an existing AngularJS application to a hybrid application, to begin incrementally upgrading it to Angular 4. I want to understand how Angular 4 needs to be referenced in the existing AngularJS. In the existing AngularJS application, it's clear how the AngularJS framework is being loaded - it's simply an included script file:
<script src="/angular/angular.js"></script>
To familiarise myself with an up to date Angular version, I've created a separate 'quickstart' Angular 5 application using the Angular quickstart guide at https://angular.io/guide/quickstart, and I can run it using:
ng serve --open
When I look at the project files though, I'm not seeing where the angular framework is actually being loaded. There is no script being included anywhere in the src/index.html file for that application, it simply declares an directive for a component (app.component.ts) that looks like this:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Test Angular 5 app';
}
Where does the import actually import from? The Angular documentation says about the index.html file that it is:
The main HTML page that is served when someone visits your site. Most
of the time you'll never need to edit it. The CLI automatically adds
all js and css files when building your app so you never need to add
any or tags here manually.
So what is happening here? How is #angular/core actually being referenced?
When using Angular CLI, we have a lot of moving parts. To really understand what's happening, we should review some of the core technologies used in Angular CLI as they relate to output files and module resolution:
TypeScript -- will transpile your TypeScript code to ES5 or ES6 (most commonly ES5). The transpiled code will use CommonJS modules by default if the target is ES5. If the target is ES6, then ES6 modules will be used.
Webpack -- will take your transpiled TypeScript code and build a dependency graph of your import / exports based on the entry points defined in webpack.config.js. This dependency graph will include all of your modules and it will package these modules into 1+ bundles, depending on the configuration. This is the step where the #angular/core dependency (which lives in node_modules/#angular/core) is processed by Webpack and added to a bundle that can be accessed at runtime.
You can load the generated bundles into the page by including the generated files in your HTML. In the case of JS files, you load them using the script tag like you did with AngularJS.
Because we are using Angular CLI, there are a lot of configurations within Webpack that are set up by default, so we will have multiple generated bundles.
I just created an empty Angular CLI project and inspected the HTML to see the 5 generated bundles:
At the core of the complexity when comparing Angular project files with AngularJS files is that Angular code is transformed through multiple build steps, while AngularJS code is normally used out-of-the-box with ES5. If you were to add Webpack and TypeScript to your AngularJS build, you would see something very similar to the Angular output.
Where is Webpack Configured in Angular CLI?
Under the hood, Angular CLI uses Webpack to build your project and bundle your files.
The Angular team chose not to expose a configurable Webpack configuration file for Angular CLI, but they did add ng eject, which generates a webpack.config.js that matches the Webpack build of your project. The downside of ejecting is that you will no longer use ng serve to serve your project. Instead, you'll use npm run build && npm run start (you'll see these scripts added to your package.json after you eject), which will build and serve your project based on the generated webpack.config.js file.
This feature is essential if you need to make custom modifications to the default Webpack build.
Read more about ng eject here.
Where are the generated bundles?
If you are using ng serve, you won't see any of your generated files because these are being served from memory and not from disk (to speed up development when files are constantly changing), so your generated files are not located in any folder.
If you want to see your generated files, you can run ng build, which will create a dist folder with the index.html and associated assets/bundles.
Note that by default, all commands that build or serve your project will delete your dist folder unless you pass the --no-delete-output-path when building/serving.
You need to understand how imports and exports work in TypeScript
From TypeScript Docs
Exporting a declaration
Any declaration (such as a variable, function, class, type alias, or
interface) can be exported by adding the export keyword.
Validation.ts
export interface StringValidator {
isAcceptable(s: string): boolean;
}
ZipCodeValidator.ts
export const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
Export statements
Export statements are handy when exports need to be renamed for
consumers, so the above example can be written as:
ZipCodeValidator.ts
class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };
Import
Importing is just about as easy as exporting from a module. Importing
an exported declaration is done through using one of the import forms
below:
Import a single export from a module
import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();
imports can also be renamed
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();
Import the entire module into a single variable, and use it to access the module exports
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();
Since you followed the Angular quickstart guide you must have used npm which is used to install your modules and dependencies.
If you read angular.io/npm
#angular/core: Critical runtime parts of the framework needed by every
application. Includes all metadata decorators, Component, Directive,
dependency injection, and the component lifecycle hooks.
And by default npm stores all your dependencies in node_modules directory.
So you are importing Component from #angular/core which lives inside node_modules directory.
Context
I'm working on a project using Angular2, TS and SystemJS. I've built a simple module loader using SystemJS which allows me to import 'custom' modules like this :
import { NavComponent } from "component#nav";
Maybe it'll be important.
Goal
I would like to import .scss files inside my angular components to be able to do things like that :
#Component({
selector: 'app-view',
styles: [ require('path/to/mainScss/file.scss') ],
template: //...
})
Issue
I've found some examples using webpack but I can't get this works. During searching, I've found this module. It can be interesting because it uses SystemJS like me and I don't have to add webpack to my project.
So I've decided to use it (and I'm not sure if it's possible to use webpack and SystemJS at the same time. require was undefined). I didn't know jspm before using this module, I've always used npm. So I've installed jspm locally (relative to my project) and globally and I've installed the module.
Then, inside a component : import './styles/importer.scss!';. And I get an error here.
Error: SyntaxError: Unexpected token <. I know that this error happens when SystemJS failed to load a module but I can't figure out why.
Why ?!
Why am I trying to do that ?
My module loader allows me to write some modules in separate folders (named like this namepace#name). I can add those folders inside another one named modules/ and then enable/disable modules by editing a configuration file.
So I want my .scss files to be loaded only if a module is enabled. That's why I want to include my sass inside an Angular component. That way, it'll be loaded only if the component is loaded.