How can I export a flow interfaces using CommonJS? - flowtype

I'm not sure if my build is good or not, but in my package.json, I have
"scripts": {
"build": "flow-remove-types src/ -d build/",
And I have a types/sentence.js, which has:
// #flow
type SentenceObject = {
foo: number,
bar: boolean,
baz: string,
};
module.exports = {
SentenceObject
};
And in my library file, I have:
const { SentenceObject } = require('../interfaces/sentence');
The problem, when I do yarn build is:
src/interfaces/sentence.js
↳ Syntax Error: Unexpected token, expected = (3:27)
2: export type SentenceObject {
What am I doing incorrectly?

There seems to no CommonJS style for require flow types. Instead you can use export/import as suggested in docs. So in your case it might be something like:
types/sentence.js:
// #flow
export type SentenceObject = {
foo: number,
bar: boolean,
baz: string,
};
notice the export keyword.
And in your library file you can use import type {...} from '':
import type { SentenceObject } from '../interfaces/sentence';

Related

Vue3 testing ElementPlus controls with vitest

I am running Vue3 with vite and can't write any tests for components that use the ElementPlus library. Something else needs to be injected apparently but I don't know how to do that.
I have the following dateControl.test.js:
import { describe, expect, test } from 'vitest';
import { ref } from 'vue';
import DateCtrl from '#/components/DateCtrl.vue';
import { mount } from "#vue/test-utils";
import ElementPlus from "element-plus";
describe("DateCtrl.vue", () => {
const messages = {
"en-US" : {
strings: {
placeholder: 'a',
label: 'b'
}
}
};
const locale = "en-US";
const data = ref ({
date: ''
});
test ("Arrange DateCtrl", async () => {
const component = mount(DateCtrl, {
props: {
vModel: data.value.date,
modelValue: data.value.date,
labelLoc: "label",
className: "w1x5",
placeholderLoc: "date"
},
global: {
plugins: [ElementPlus],
mocks: {
$t: (msg) => {
const params = msg.split('.');
return messages[locale][params[0]][params[1]];
}
}
}
});
//fails on previous lines.
expect(typeof component !== "undefined", "component created").toBeTruthy();
let h3Text = component.findAll('h3')[0].element.innerHTML;
expect(component.findAll('.form').length === 1, "form element rendered").toBeTruthy();
expect(h3Text === "d", "locale strings correct").toBeTruthy();
});
});
It doesn't even get to the "expect" tests, fails with message:
Error: Cannot find module 'C:\source\mySite\node_modules\dayjs\plugin\customParseFormat'
imported from
C:\source\mySite\node_modules\element-plus\es\components\date-picker\src\date-picker.mjs
Did you mean to import dayjs/plugin/customParseFormat.js?
This bit seems to indicate that node expects you to use .js extension and element is not doing that.
Error: Cannot find module 'C:\source\mySite\node_modules\dayjs\plugin\customParseFormat'
imported from
C:\source\mySite\node_modules\element-plus\es\components\date-picker\src\date-picker.mjs
Did you mean to import dayjs/plugin/customParseFormat.js?
I'm guessing this is because you may be running an older node version. Element requires at least node v16.
I have this problem too.
It seems that this problem is already solved in this pull request - https://github.com/element-plus/element-plus/pull/6811

How to get storybook addon options

When we load a storybook addon we can pass some options:
// .storybook/main.js
module.exports = {
addons: [
'a-storybook-addon-without-options',
{
name: 'a-storybook-addon-with-options',
options: {
mainColor: 'hotpink',
},
},
],
};
To write my addon I use the addon API:
// /a-storybook-addon-with-options/src/register.js
import React from 'react';
import { addons, types } from '#storybook/addons';
import MyComponent from './myComponent.js';
const ADDON_ID = 'myaddon';
const PANEL_ID = `${ADDON_ID}/panel`;
addons.register(ADDON_ID, (api) => {
addons.add(PANEL_ID, {
type: types.PANEL,
title: 'My Addon',
render: MyComponent,
});
});
I don't know how to get the developer options from the addon code. Is there an official way? I didn't get a clear help from the documentation.
I think here you should use a parameter instead of this option in the config.
This kind of config (the options field) is more for presets that alter webpack or babel config.
If you add a parameter in the preview with the key that you specify in the register file then you can easily access that parameter in the addon code.
in preview.js
export const parameters = {
myAddon: {
mainColor: 'red'
},
};
in the addon code
import { useParameter } from '#storybook/api';
const PARAM_KEY = 'myAddon';
const MyPanel = () => {
const value = useParameter(PARAM_KEY, null);
const mainColor = value ? value.mainColor : 'No mainColor defined';
return <div>{mainColor}</div>;
};
You could set some default by setting a value when they don't set any value on the parameter like:
const mainColor = value ? value.mainColor : 'red';

including media queries to styled components theme with typescript

I m using styled components with React typescript. I have created a theme.ts where I define my variables that i use in my ThemeProvider to expose them to my application.
In addition I have a styled.d.ts, where I declare my DefaultTheme that I use as an interface for theme.ts.
I have created a separate file. media.ts where I create reusable media queries as described in this article: https://medium.com/#samuelresua/easy-media-queries-in-styled-components-690b78f50053,
and now I want to include ...media in my theme so I can use it with my theme:
media.ts
import { css } from 'styled-components'
interface MediaQueryProps {
[key: string]: any;
}
const breakpoints: MediaQueryProps = {
xs: 480,
sm: 768,
md: 992,
lg: 1200
}
export default Object.keys(breakpoints).reduce((acc, label) => {
acc[label] = (literals: TemplateStringsArray, ...placeholders: any[]) => css`
#media (max-width: ${breakpoints[label]}px) {
${css(literals, ...placeholders)};
}
`.join("");
return acc
}, {} as Record<keyof typeof breakpoints, (l: TemplateStringsArray, ...p: any[])=> string>)
theme.ts
import { DefaultTheme } from 'styled-components'
import media from './media';
const theme: DefaultTheme = {
primary: "#262646",
secondary: "#F9FAFE",
success: "#00897B",
danger: "#B00020",
text: "#263646",
background: "#E8EAF6",
white: "#ffffff",
fonts: ["sans-serif", "Roboto"],
...media,
fontSizes: {
small: "1em",
medium: "1.2em",
},
}
export default theme;
styled.d.ts
// import original module declarations
import 'styled-components'
// and extend them!
declare module 'styled-components' {
export interface DefaultTheme {
primary: string,
secondary: string,
success: string,
danger: string,
text: string,
background: string,
white: string,
fonts: Array<string>,
fontSizes: {
small: string,
medium: string,
},
}
}
If I try to use my mediaqueries in my App.ts, without including ...media in DefaultTheme, it throws error
'Property 'media' does not exist on type 'DefaultTheme'.ts(2339)',
const TableRow = styled.tr`
&:nth-of-type(odd){
background: ${props => props.theme.secondary}
}
${({theme}) => theme.media.sm`
background: ${theme.secondary}
`}
`
but if i define it as following it throws error
declare module 'styled-components' {
export interface DefaultTheme {
media: Array<string>
}
}
it throws and error in theme.ts
'Property 'media' is missing in type '{ primary: string; secondary: string; success: string; danger: string; text: string; background: string; white: string; fonts: string[]; fontSizes: { small: string; medium: string; }; }' but required in type 'DefaultTheme'.ts(2741)'
I m completely lost about how to define it in a right way for it to work.
when I hover over media I get following information form typescript:
(alias) const media: Record string>
help pliz...
I think your problem is that you defined a theme interface where you didn't defined the fields exported from media, but you are trying to use the them in the default theme, which type is Them which does not have the media fields.
The easiest way is to define a media filed in your interface, type any and put the media queries inside the media field. I am not sure if that work for you or if it's a good practice. I haven't work a lot with this type of theming, but I think something like this should do the job.

React, how to import all css files in a folder? [duplicate]

With ES6, I can import several exports from a file like this:
import {ThingA, ThingB, ThingC} from 'lib/things';
However, I like the organization of having one module per file. I end up with imports like this:
import ThingA from 'lib/things/ThingA';
import ThingB from 'lib/things/ThingB';
import ThingC from 'lib/things/ThingC';
I would love to be able to do this:
import {ThingA, ThingB, ThingC} from 'lib/things/*';
or something similar, with the understood convention that each file contains one default export, and each module is named the same as its file.
Is this possible?
I don't think this is possible, but afaik the resolution of module names is up to module loaders so there might a loader implementation that does support this.
Until then, you could use an intermediate "module file" at lib/things/index.js that just contains
export * from 'ThingA';
export * from 'ThingB';
export * from 'ThingC';
and it would allow you to do
import {ThingA, ThingB, ThingC} from 'lib/things';
Just a variation on the theme already provided in the answer, but how about this:
In a Thing,
export default function ThingA () {}
In things/index.js,
export {default as ThingA} from './ThingA'
export {default as ThingB} from './ThingB'
export {default as ThingC} from './ThingC'
Then to consume all the things elsewhere,
import * as things from './things'
things.ThingA()
Or to consume just some of things,
import {ThingA,ThingB} from './things'
The current answers suggest a workaround but it's bugged me why this doesn't exist, so I've created a babel plugin which does this.
Install it using:
npm i --save-dev babel-plugin-wildcard
then add it to your .babelrc with:
{
"plugins": ["wildcard"]
}
see the repo for detailed install info
This allows you to do this:
import * as Things from './lib/things';
// Do whatever you want with these :D
Things.ThingA;
Things.ThingB;
Things.ThingC;
again, the repo contains further information on what exactly it does, but doing it this way avoids creating index.js files and also happens at compile-time to avoid doing readdirs at runtime.
Also with a newer version you can do exactly like your example:
import { ThingsA, ThingsB, ThingsC } from './lib/things/*';
works the same as the above.
You now can use async import():
import fs = require('fs');
and then:
fs.readdir('./someDir', (err, files) => {
files.forEach(file => {
const module = import('./' + file).then(m =>
m.callSomeMethod();
);
// or const module = await import('file')
});
});
Great gugly muglys! This was harder than it needed to be.
Export one flat default
This is a great opportunity to use spread (... in { ...Matters, ...Contacts } below:
// imports/collections/Matters.js
export default { // default export
hello: 'World',
something: 'important',
};
// imports/collections/Contacts.js
export default { // default export
hello: 'Moon',
email: 'hello#example.com',
};
// imports/collections/index.js
import Matters from './Matters'; // import default export as var 'Matters'
import Contacts from './Contacts';
export default { // default export
...Matters, // spread Matters, overwriting previous properties
...Contacts, // spread Contacts, overwriting previosu properties
};
// imports/test.js
import collections from './collections'; // import default export as 'collections'
console.log(collections);
Then, to run babel compiled code from the command line (from project root /):
$ npm install --save-dev #babel/core #babel/cli #babel/preset-env #babel/node
(trimmed)
$ npx babel-node --presets #babel/preset-env imports/test.js
{ hello: 'Moon',
something: 'important',
email: 'hello#example.com' }
Export one tree-like default
If you'd prefer to not overwrite properties, change:
// imports/collections/index.js
import Matters from './Matters'; // import default as 'Matters'
import Contacts from './Contacts';
export default { // export default
Matters,
Contacts,
};
And the output will be:
$ npx babel-node --presets #babel/preset-env imports/test.js
{ Matters: { hello: 'World', something: 'important' },
Contacts: { hello: 'Moon', email: 'hello#example.com' } }
Export multiple named exports w/ no default
If you're dedicated to DRY, the syntax on the imports changes as well:
// imports/collections/index.js
// export default as named export 'Matters'
export { default as Matters } from './Matters';
export { default as Contacts } from './Contacts';
This creates 2 named exports w/ no default export. Then change:
// imports/test.js
import { Matters, Contacts } from './collections';
console.log(Matters, Contacts);
And the output:
$ npx babel-node --presets #babel/preset-env imports/test.js
{ hello: 'World', something: 'important' } { hello: 'Moon', email: 'hello#example.com' }
Import all named exports
// imports/collections/index.js
// export default as named export 'Matters'
export { default as Matters } from './Matters';
export { default as Contacts } from './Contacts';
// imports/test.js
// Import all named exports as 'collections'
import * as collections from './collections';
console.log(collections); // interesting output
console.log(collections.Matters, collections.Contacts);
Notice the destructuring import { Matters, Contacts } from './collections'; in the previous example.
$ npx babel-node --presets #babel/preset-env imports/test.js
{ Matters: [Getter], Contacts: [Getter] }
{ hello: 'World', something: 'important' } { hello: 'Moon', email: 'hello#example.com' }
In practice
Given these source files:
/myLib/thingA.js
/myLib/thingB.js
/myLib/thingC.js
Creating a /myLib/index.js to bundle up all the files defeats the purpose of import/export. It would be easier to make everything global in the first place, than to make everything global via import/export via index.js "wrapper files".
If you want a particular file, import thingA from './myLib/thingA'; in your own projects.
Creating a "wrapper file" with exports for the module only makes sense if you're packaging for npm or on a multi-year multi-team project.
Made it this far? See the docs for more details.
Also, yay for Stackoverflow finally supporting three `s as code fence markup.
Similar to the accepted answer but it allows you to scale without the need of adding a new module to the index file each time you create one:
./modules/moduleA.js
export const example = 'example';
export const anotherExample = 'anotherExample';
./modules/index.js
// require all modules on the path and with the pattern defined
const req = require.context('./', true, /.js$/);
const modules = req.keys().map(req);
// export all modules
module.exports = modules;
./example.js
import { example, anotherExample } from './modules'
If you are using webpack. This imports files automatically and exports as api namespace.
So no need to update on every file addition.
import camelCase from "lodash-es";
const requireModule = require.context("./", false, /\.js$/); //
const api = {};
requireModule.keys().forEach(fileName => {
if (fileName === "./index.js") return;
const moduleName = camelCase(fileName.replace(/(\.\/|\.js)/g, ""));
api[moduleName] = {
...requireModule(fileName).default
};
});
export default api;
For Typescript users;
import { camelCase } from "lodash-es"
const requireModule = require.context("./folderName", false, /\.ts$/)
interface LooseObject {
[key: string]: any
}
const api: LooseObject = {}
requireModule.keys().forEach(fileName => {
if (fileName === "./index.ts") return
const moduleName = camelCase(fileName.replace(/(\.\/|\.ts)/g, ""))
api[moduleName] = {
...requireModule(fileName).default,
}
})
export default api
I've used them a few times (in particular for building massive objects splitting the data over many files (e.g. AST nodes)), in order to build them I made a tiny script (which I've just added to npm so everyone else can use it).
Usage (currently you'll need to use babel to use the export file):
$ npm install -g folder-module
$ folder-module my-cool-module/
Generates a file containing:
export {default as foo} from "./module/foo.js"
export {default as default} from "./module/default.js"
export {default as bar} from "./module/bar.js"
...etc
Then you can just consume the file:
import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()
Just an other approach to #Bergi's answer
// lib/things/index.js
import ThingA from './ThingA';
import ThingB from './ThingB';
import ThingC from './ThingC';
export default {
ThingA,
ThingB,
ThingC
}
Uses
import {ThingA, ThingB, ThingC} from './lib/things';
Nodejs ? Do like this:
Create a folder with index.js, in index file, add this:
var GET = require('./GET');
var IS = require('./IS');
var PARSE = require('./PARSE');
module.exports = { ...GET, ...IS, ...PARSE};
And, in file GET.js, or IS.js export as normal:
module.exports = { /* something as you like */}
ANd now, you need only including index.js like:
const Helper = require('./YourFolder');
Helper will include all of function in YourFolder.
Good day!
This is not exactly what you asked for but, with this method I can Iterate throught componentsList in my other files and use function such as componentsList.map(...) which I find pretty usefull !
import StepOne from './StepOne';
import StepTwo from './StepTwo';
import StepThree from './StepThree';
import StepFour from './StepFour';
import StepFive from './StepFive';
import StepSix from './StepSix';
import StepSeven from './StepSeven';
import StepEight from './StepEight';
const componentsList= () => [
{ component: StepOne(), key: 'step1' },
{ component: StepTwo(), key: 'step2' },
{ component: StepThree(), key: 'step3' },
{ component: StepFour(), key: 'step4' },
{ component: StepFive(), key: 'step5' },
{ component: StepSix(), key: 'step6' },
{ component: StepSeven(), key: 'step7' },
{ component: StepEight(), key: 'step8' }
];
export default componentsList;
You can use require as well:
const moduleHolder = []
function loadModules(path) {
let stat = fs.lstatSync(path)
if (stat.isDirectory()) {
// we have a directory: do a tree walk
const files = fs.readdirSync(path)
let f,
l = files.length
for (var i = 0; i < l; i++) {
f = pathModule.join(path, files[i])
loadModules(f)
}
} else {
// we have a file: load it
var controller = require(path)
moduleHolder.push(controller)
}
}
Then use your moduleHolder with dynamically loaded controllers:
loadModules(DIR)
for (const controller of moduleHolder) {
controller(app, db)
}
I was able to take from user atilkan's approach and modify it a bit:
For Typescript users;
require.context('#/folder/with/modules', false, /\.ts$/).keys().forEach((fileName => {
import('#/folder/with/modules' + fileName).then((mod) => {
(window as any)[fileName] = mod[fileName];
const module = new (window as any)[fileName]();
// use module
});
}));
if you don't export default in A, B, C but just export {} then it's possible to do so
// things/A.js
export function A() {}
// things/B.js
export function B() {}
// things/C.js
export function C() {}
// foo.js
import * as Foo from ./thing
Foo.A()
Foo.B()
Foo.C()

"Ineligible value used in/as type annotation" error with Flow

I have a JS file:
// #flow
import type SomeType from './types';
export class MyClass extends React.Component {
props: {
someProp: Array<SomeType>
};
}
In types.js:
// #flow
export type SomeType = {
someKey: number
}
Running flow gives me the following error on Array<SomeType>:
exports. Ineligible value used in/as type annotation (did you forget 'typeof'?)
This is me being an ES6 noob. The fix is in how the type is imported. It should be:
import type {SomeType} from './types';

Resources