I am learning to create npm packages by creating a session check function sessionFn that will popup a modal 1 minute before the session expires.
The function works as expected on the main app (a nuxtJS app) but when I make use it as an npm module I have to pass moment as an argument even though moment is listed as a dependency and is imported on the module.
I am missing something, why is my module not picking up moment? I want to use the module like sessionFn(this, to.path); instead of sessionFn(this, to.path, moment); moment is undefined when I don't pass it
Package files
package.json
{
"name": "hello-stratech",
"version": "1.0.17",
"description": "Hello Stratech",
"main": "index.js",
"keywords": [
"npm",
"hello",
"stratech"
],
"author": "Simo Mafuxwana",
"license": "ISC",
"dependencies": {
"moment": "^2.22.2"
}
}
index.js (main js file)
import moment from "moment";
module.exports = {
greeting(name) {
alert("Hello.. " + name);
},
department(dev) {
...
},
sessionFn(context) {
const exp = context.$store.state.session.exp;
let userSystemTime = new Date();
userSystemTime = moment.utc(userSystemTime)
const diff = moment(userSystemTime).diff(moment(exp), 'minutes');
if (diff = 1) {
// open modal
}
}
}
Usage
This is how I use the package in the main app
import moment from 'moment';
import { sessionFn } from "hello-stratech";
export default {
...
watch: {
$route(to) {
sessionFn(this, to.path, moment);
}
}
...
}
You dont need to import and pass moment into your function since you are importing it in your function file. You are not even using the passed argument in your function. So you can just safely dont pass it. You are also not using second argument that u pass to.path, so u can omit it too.
As a suggestion you should install and use eslint, which will catch such things. You can setup a nuxt project with eslint using create nuxt app for example.
There is also a bug in esm 3.21-3.22 that prevent commonjs and es6 imports work together https://github.com/standard-things/esm/issues/773 . That should be fixed when a new esm will be released
Try making moment as devDependancy through npm i moment --save-dev instead of dependency.
That way moment will only be needed when the package is development (means when you are developing the project) but not when it is used.
Hope it fixes your issue
for more depth knowledge
Related
I use t() function to translate text.
The function is acting like there are no locales in astros /public folder.
My file structure
My translation.json file for en:
{
"index": {
"testHeader": "Test Header"
}
}
Here is my index page code:
---
import Layout from "../layouts/Layout.astro";
import { t, changeLanguage } from "i18next";
changeLanguage("en");
---
<Layout>
<h1>{t("index.testHeader")}</h1>
</Layout>
My astro-i18next.config.mts:
/** #type {import('astro-i18next').AstroI18nextConfig} */
export default {
defaultLocale: "en",
locales: ["en", "cs"],
};
My astro.config.mjs:
import { defineConfig } from 'astro/config';
import astroI18next from "astro-i18next";
import tailwind from '#astrojs/tailwind';
// https://astro.build/config
import react from "#astrojs/react";
// https://astro.build/config
export default defineConfig({
integrations: [astroI18next(), react(), tailwind({
config: './tailwind.config.cjs',
})]
});
the t() function shows the passed key instead of translation.
I runned npx astro-i18next generate which did nothing
I had a similar issue; I fixed it with a config change and a downgrade.
(since it's still in beta, gotta keep an eye on that)
NOTE: The current version of "astro-i18next" is "1.0.0-beta.17".
Add the following to your astro-i18next.config.*: baseLanguage: "en"
Downgrade your version to 1.0.0-beta.13, between versions 10 to 17 only this one worked for me.
Good but not Necessary: add this to your package.json scripts: "i18n": "npx astro-i18next generate"
Run this command and should be successful: pnpm i && pnpm run i18n && pnpm run build
Considering this fix, I'm looking forward for similar issues to be resolved in stable release; However, for the time being this should get you going.
I fixed it using npm update.
For some reason my app's dependencies weren't updated.
I am new to pnpm workspaces and am trying to resolve the following issue.
My demo project:
root
packages
common-ui
main-lib
common-ui is a Vite based package containing some Vue components that can be reused by other packages, in my example it's being used by main-lib.
"dependencies": {
"ui-common": "workspace:*"
},
common-ui is referencing an index.ts inside its package.json
"main": "index.ts",
index.ts is exporting my Vue components:
...
export { default as Heading } from './components/Heading/Heading.vue';
...
Now I am able to import those components inside main-lib:
import { Heading } from 'common-ui'
This all works fine but I would also like to be able to publish my library to the npm registry. As common-ui is using the Vite, it's possible to build in library mode: https://vitejs.dev/guide/build.html#library-mode. My package inside common-ui will need to change to:
{
"name": "common-ui",
"files": ["dist"],
"main": "./dist/common-ui.umd.js",
"module": "./dist/common-ui.es.js",
"exports": {
".": {
"import": "./dist/common-ui.es.js",
"require": "./dist/common-ui.umd.js"
}
}
}
main is not referencing index.ts anymore but a dist folder that only gets updated when the vite command is ran. Is there a way for me to support both publishing/versioning and referencing the actual source code from inside main-lib?
I've taken a quick look at Rush.js but I am not sure if provides a solution and I want to be sure before I continue on that path.
I'm trying to use the optimized, es6 build of Mobx, as per the documentation:
Tip: the main entry point of the MobX 5 package ships with ES5 code for backward compatibility with all build tools. But since MobX 5 runs only on modern browsers anyway, consider using the faster and smaller ES6 build: lib/mobx.es6.js. For example by setting up a webpack alias: resolve: { alias: { mobx: __dirname + "/node_modules/mobx/lib/mobx.es6.js" }}
https://mobx.js.org/README.html#browser-support
This allows me to import mobx and get the mobx.es6.js build:
import mobx from 'mobx' // Yay, es6 build!!!
This works great for Webpack-based projects, such as Electron ones, where I already have it working.
For React Native, I can specify extraNodeModules in metro.config.js like so:
module.exports = {
resolver: {
extraNodeModules: {
"mobx": path.resolve(__dirname, 'node_modules/mobx/lib/mobx.es6.js'),
},
},
};
...except that doesn't work, I presume, because the mobx dependency resolves fine on its own, and so this configuration option is never checked.
I can use a separate alias for mobx, such as mobx-es6 but that's not ideal, to put it nicely:
module.exports = {
resolver: {
extraNodeModules: {
// Nooo I don't want to update a bazillion source files!.
"mobx-es6": path.resolve(__dirname, 'node_modules/mobx/lib/mobx.es6.js'),
},
},
};
Is there some other way to configure Metro so that I can override the mobx import like I can with Webpack?
I'm using RN 0.60.0.
The solution is to add a browser section to package.json:
"name": "My React Native Project",
"version": "0.0.1",
"browser": {
"mobx": "mobx/lib/mobx.es6.js"
},
This is undocumented, AFAICT, but there are hints here:
resolverMainFields
Type: Array<string>
Specify the fields in package.json files that will be used by the module resolver to do redirections when requiring certain packages. For example, using ['browser', 'main'] will use the browser field if it exists and will default to main if it doesn't.
https://facebook.github.io/metro/docs/configuration#resolvermainfields
import mobx from 'mobx' // Yay, es6 build!!!
I'm building a web (react with webpack & babel) and mobile apps (react-native with expo) for a project. I therefore created a common library for business logic and redux/api library.
Some code will be slightly different between web and mobile. In my case it's localStorage vs AsyncStorage, which I use for authentication among other things...
I'm trying to pass an environment variable for the build stage to switch import of certain files so that the correct file is loaded for each build which are simply path linked (ie no pre-build of my library, I just do import '../mylib') ex:
if(PLATFORM === 'mobile'){
import StorageModule from './mobile-storage-module`
} else {
import StorageModule from './mobile-storage-module`
}
export default StorageModule
Try 1
#babel/preset-env to say if it's mobile or web so that it imports different libraries depending on build like so:
My .babelrc has this:
{
"presets": [
[
"#babel/preset-env",
{
"platform": "mobile"
}
]
]
}
And then in local storage file I do this:
export default () => {
const platform = process.env.platform
if (platform === 'mobile') {
return import './storage-modules/storage-mobile'
}
return import './storage-modules/storage-web'
}
That didn't work, and this also didn't work for me.
Try 2
I installed react-native-dotenv and created a .env file with:
PLATFORM=mobile
And set the plugin in my .babelrc:
{
"presets": [
"babel-preset-expo",
"react-native-dotenv"
]
}
And in my example file, I tried this:
import { PLATFORM } from 'react-native-dotenv'
export default PLATFORM === 'mobile' ? import './storage-modules/storage-mobile' : import './storage-modules/storage-web'
But now my build doesn't work. Any idea how I do dynamic imports during the build process that works for babel in react-native app and webpack build (also uses babel)?
First, #babel/preset-env does not do what you think it does. This is not for specifying your own variables, it is a plugin to automatically use the right target and pollyfills for the browsers you want to support.
The easiest way to get environment variables is with the webpack define plugin (which is part of webpack, so no need to install anything extra)
Just add this to your webpack config.
plugins: [
new webpack.DefinePlugin({
'process.env': {
platform: 'mobile',
},
}),
],
Next, you can't use normal import statements inside of ifs.
import gets resolved before any code runs, either on build by webpack, or in supported environments on script load.
To import something on runtime, you need to use dynamic imports.
Here is an example of how this could look like.
export default new Promise(async resolve => {
resolve(
process.env.platform === 'mobile'
? (await import('./mobile.js')).default
: (await import('./desktop.js')).default
);
});
You can now import from this file like you normally would, but be aware that the default export is a promise.
As your question's title says "during babel build phase", I assume you would like to make different builds for desktop and mobile (not one build for both and load the needed modules dynamically run-time). So I would go like this:
Define the run scripts in package.json for desktop and mobile:
"scripts": {
"devmobile": "cross-env NODE_ENV=development PLATFORM=mobile webpack --progress",
"dev": "cross-env NODE_ENV=development webpack --progress",
}
... or you can create two different webpack.config.js files for desktop and mobile builds but I think the above is easier...
Then npm run devmobile to build for mobile and npm run dev for desktop.
Since I'm on Windows I use the cross-env package but this is the recommended way to be OS independent.
Then I would use Webpack's NormalModuleReplacementPlugin:
(based on this exmaple)
In your webpack.config.js:
// defining the wanted platform for the build (comfing form the npm run script)
const targetPlatform = process.env.PLATFORM || 'desktop';
// then use the plugin like this
plugins: [
new webpack.NormalModuleReplacementPlugin(/(.*)-PLATFORM(\.*)/, function(resource) {
resource.request = resource.request.replace(/-PLATFORM/, `-${targetPlatform}`);
}),
]
...then if you have these two files:
./storage-modules/storage-mobile.js
./storage-modules/storage-desktop.js
import the needed one in your script like this:
import './storage-modules/storage-PLATFORM';
This way the generated build will only contain the needed file for the current PLATFORM used for the build process.
Another possible solution could be the ifdef-loader but I haven't tested it. Maybe worth to try, seems easy.
If you want one build though and import the needed module dynamically, you could do something like this in your app.js (or whatever):
// this needs to have defined when the app is running
const targetPlatform = process.env.PLATFORM || 'desktop';
import(
/* webpackChunkName: "[request]" */
`./storage-modules/storage-${targetPlatform}`
).then(storageModule => {
// use the loaded module
});
or:
(async () => {
const storageModule = await import(
/* webpackChunkName: "[request]" */
`./storage-modules/storage-${targetPlatform}`
);
// use the loaded module
})();
For this to work Babel has to be configured.
More on Webpack with dynamic imports here.
You can use transform-inline-environment-variablesto pass platform to babel
"build-mobile": "PLATFORM=mobile ...",
"build-app": "PLATFORM=app ...",
I'm trying to move one of my custom elements into a plug-in so that I can re-use it across projects.
I had a look at the skeleton plugin and noticed that it has a src/index.js that returns a config with all custom elements defined as globalResources.
So I tried the same thing and I basically have:
src/index.js
export function configure (config) {
config.globalResources([
'./google-map',
'./google-map-location-picker',
'./google-map-autocomplete'
]);
}
And then I have each one of my custom elements next to index.js, for example:
google-map.js
import {inject, bindable, bindingMode, inlineView} from 'aurelia-framework';
#inlineView(`
<template>
<div class="google-map"></div>
</template>
`)
#inject(Element)
export class GoogleMapCustomElement {
// All the Custom Element code here
}
I've also set up a basic npm script that runs babel on the code and sticks it in dist/:
"main": "dist/index.js",
"babel": {
"sourceMap": true,
"moduleIds": false,
"comments": false,
"compact": false,
"code": true,
"presets": [ "es2015-loose", "stage-1"],
"plugins": [
"syntax-flow",
"transform-decorators-legacy",
"transform-flow-strip-types"
]
},
"scripts": {
"build": "babel src -d dist"
},
Tbh I'm not entirely sure this is all correct but I took some of it from the skeleton plugin and it seems to run fine.
Anyway, the problem I'm having is that after I install the plugin (npm install --save-dev powerbuoy/AureliaGoogleMaps), add it to my aurelia.json in build.bundles[vendor-bundle.js].dependencies and tell aurelia to use it in main.js (.use.plugin('aurelia-google-maps')) I get:
GET http://localhost:9000/node_modules/aurelia-google-maps/dist/index/google-map.js (404)
So my question is, where does it get the dist/index/ part from?? I'm configuring my globalResources in index.js but nowhere does it say that I have an index folder.
What am I doing wrong?
Bonus question: What is the bare minimum required to transpile my ES6 plug-in code so that others can use it? Does my babel configuration look correct?
What about referencing your plugin within aurelia.json, like this:
{
"name": "aurelia-google-maps",
"path": "../node_modules/aurelia-google-maps/dist",
"main": "index"
}
I have absolutely no idea why, but in order to solve this problem I actually had to move my custom elements inside an index/ folder.
So now I have this:
- index.js
- index/
- custom-element-one.js
- custom-element-two.js
And my index.js still looks like this:
export function configure (config) {
config.globalResources([
'./custom-element-one',
'./custom-element-two'
]);
}
Where it gets index/ from I guess I will never know, but this works at least.
I did need the babel plug-in Marton mentioned too, but that alone did not solve the mystery of the made up path.
Edit: To elaborate a bit further, if I name my main entry point something other than index.js the folder too needs that name. For example, if I were to rename index.js main.js I would need to put my globalResources inside a folder called main/.
Update:
Edit: thanks for clarifying why you don't want to use the whole skeleton-plugin package.
Focusing on your original question: aurelia-cli uses RequireJS (AMD format) to load dependencies. Probably, your current output has a different format.
Add transform-es2015-modules-amd to babel.plugins to ensure AMD-style output, so it will be compatible with RequireJS and therefore with aurelia-cli.
"babel": {
"sourceMap": true,
"moduleIds": false,
"comments": false,
"compact": false,
"code": true,
"presets": [ "es2015-loose", "stage-1"],
"plugins": [
"syntax-flow",
"transform-decorators-legacy",
"transform-flow-strip-types",
"transform-es2015-modules-amd"
]
}
Original:
There are several blog post about plugin creation, I started with this: http://patrickwalters.net/making-out-first-plugin/ .
Of course, there have been many changes since then, but it's a useful piece of information and most of it still applies.
I'd recommend using plugin-skeleton as project structure. It provides you with a working set of gulp, babel, multiple output formats out-of-the-box.
With this approach, your plugin's availability wouldn't be limited to JSPM or CLI only but everyone would have the possibility to install it regardless of their build systems.
Migration is fairly easy in your case:
Download skeleton-plugin
Copy your classes + index.js into src/
npm install
...wait for it...
gulp build
check dist/ folder
most of your pain should now be gone :)
Here are some details based on my observations/experience.
1. Main index.js/plugin-name.js:
In general, a main/entry point is required, where the plugin's configure() method is placed. It serves as a starting point when using it within an Aurelia application. This file could have any name, usually it's index.js or plugin-name.js (e.g. aurelia-google-maps.js) to make it clear for other developers what should be included for bundling. Set that same entry point in package.json as well.
In addition to globalResources, you can implement a callback function to allow configuration overrides. That can be called in the application, which will use the plugin. Example solution
Plugin's index.js
export * from './some-element';
export function configure(config, callback) {
// default apiKey
let pluginConfig = Container.instance.get(CustomConfigClass);
pluginConfig.apiKey = '01010101';
// here comes an override
if (callback) {
callback(pluginConfig);
}
...
config.globalResources(
'./some-element'
);
}
Your app's main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin('aurelia-google-maps', (pluginConfig) => {
// custom apiKey
pluginConfig.apiKey = '12345678';
});
aurelia.start().then(a => a.setRoot());
}
2. HTML and CSS resources:
If you have html only custom elements, you can make them available using globalResources.
Custom css styling is going to require a bit of additional configuration in bundling configuration (see below).
3. Using the plugin with aurelia-cli: Documentation
One of the first new features you'll see soon is a command to help you with 3rd party module configuration. The command will inspect a previously npm-installed package, and make a configuration recommendation to you, automating the process if you desire.
While we are looking forward to that above moment, let's edit aurelia.json:
Configure plugin dependencies. If there are any external libraries (e.g. Bootstrap), then those should be included before your plugin.
Include your plugin:
...
{
"name": "plugin-name",
"path": "../node_modules/plugin-name/dist/amd",
"main": "plugin-name",
"resources": ["**/*.html", "**/*.css"] // if there is any
},
...
Now, your plugin is ready to include it in main.js as showed in Section 1..
I hope you didn't get sick of reading the word 'plugin' so many (21!) times. :D