import { h } from preact - when is it necessary - preact

I'm currently building a Preact PWA with the CLI.
My understanding was that wherever I have a component defined with JSX, I need to have import { h } from 'preact' at the top of the file.
I removed all instances of that import statement, yet the application still runs and builds perfectly fine.
Can someone set me straight here, as I'm a little confused now - perhaps there is some magic going on behind the scenes somewhere?

When you write a jsx syntax like
render() {
return <div id="abc">Hello World</div>
}
Behind the screen it will be converted to
render() {
return h('div', { id: 'abc' }, 'Hello World')
}
So, when it is necessary to import h?
The answer is Every time you use a jsx syntax. JSX is not part of the JavaScript spec, it have to be converted to the equivalent function call. Which in preact using h or in react using React.createElement.
As you mention, we can make the import automatic by using additional babel-plugin-jsx-pragmatic plugin.
module.exports = {
presets: [],
'plugins': [
['#babel/plugin-transform-react-jsx', { pragma: 'h' }],
['babel-plugin-jsx-pragmatic', {
module: 'preact',
import: 'h',
export: 'h',
}],
],
}
Learn more at: https://github.com/jmm/babel-plugin-jsx-pragmatic

Ok, after some digging around I see there is a babel-config in the preact-cli node module, which is adding the following code:
plugins: [
[require.resolve('babel-plugin-transform-react-jsx'), { pragma: 'h' }],
[require.resolve('babel-plugin-jsx-pragmatic'), {
module: 'preact',
export: 'h',
import: 'h'
}]
]
It appears to mean imports of h are automatic and not explicitly required. Would be nice it this were mentioned in their documentation!

Related

Unexpected node type error SequenceExpression with jest

I was adding a snapshot test to a piece of React code, and I incurred in this error:
Unexpected node type: SequenceExpression (This is an error on an internal node. Probably an internal error. Location has been estimated.)
The code transpiles and works just fine, and the AST explorer doesn't warn me about anything.
Before this new test, no other test gave me any sort of similar error, and we have quite a few of them in our codebase.
I tried to reinstall jest, reinstall babel-jest, remove and reinstall all modules (using yarn --pure-lock), upgraded both jest and babel-jest to the latest version (20.0.1 for both), rinsed and repeated.
Nothing worked.
This occurs only when I try to collect the coverage (with --coverage), while the minimal snippet it occurs with is:
import { tint } from 'polished'
import styled from 'styled-components'
export default styled.label`
background: ${({ x, y }) => (x ? tint(0.3, y.a) : y.b)};
`
Here's what i've found:
This is an issue with jest code coverage being able to understand styled components and polished. I am using babel-plugin-polished with the following in my babelrc:
"plugins": [ "polished" ]
But still if you call export on a value, and do not also use that value in an object or exported object, it will fail.
Fails:
export const charcoalBlue = rgb(104, 131, 145);
Doesn't fail:
export const charcoalBlue = rgb(104, 131, 145);
const colors = { charcoalBlue }
So my solution has been to ignore my style files, or simply ensure I'm using the values I create and not just exporting them.
One way to ignore the style files, place this in your package.json:
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!**/*.styles.js",
]
}
And name your style files {ComponentName}.styles.js
Hope this helps!
I came across the same issue!
I fixed it by working around it:
import styled, {css} from 'styled-components';
styled.label`
${({x,y}) => x
? css`background: tint(0.3, y.a);`
: css`background: ${y.b};`
}
`;

Angular2 + TypeScript + moment.js => locale are not all there (just 'en')

I am going thru a book tutorial to learn TypeScript and AngularJS 2.0:(Become_a_Ninja_with_Angular2).
At some point it explains how to make your own Pipe and goes thru an implementation of moment.js.
In the folder where my project is located I do in CLI: npm install moment
(FYI: The book also tells to do typings install --save --ambient moment-node, but that throws an error even if I change --ambient by --global, also this error happens to not be a problem to use moment.js as the rest of the code I describe below runs).
Then, as a result of previous CLI, it creates under my project folder: [my project folder]\node_modules\moment
Then in [my project folder]\main.html, I have a <script> tag with: `
<script>
System.config({
defaultJSExtensions: true,
map: {
... [plenty of stuff starting with #angular]..
'#angular/platform-browser-dynamic':'node_modules/#angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'rxjs': 'node_modules/rxjs',
'moment':'node_modules/moment/moment'
}
});
System.import('main');
</script>
My custom Pipe looks like this:
import { PipeTransform, Pipe } from '#angular/core';
import * as moment from 'moment';
import 'moment/../locale/fr';
#Pipe({name: 'fromNow'})
export class FromNowPipe implements PipeTransform {
transform(value,args){
let mydate = moment(new Date(value));
console.log(moment.locales());
return mydate.fromNow();
}
}
As you can see in my custom pipe code, to access the locale 'fr', I had to add import 'moment/../locale/fr (what I found by looking at already existing solution on StackOverflow). If this tag was not implemented I had access to 'en' only. Which means that adding other languages will require to add import 'moment/../locale/[the locale I want available].
Anyone has any ideas how to have all the locale from the lib moment.js with just the single import * as moment from 'moment'; statement?
PS: And I added to [My project folder]\app.module.ts:
import { FromNowPipe } from './custom_pipes/fromnow.pipe';
...
#NgModule({
...
declarations: [...,FromNowPipe],
...
})
...
And somewhere in one of my component I have:
[#Component({
selector: 'ns-mycomponent',
template:`
.. <div>{{ '2016/05/01'|fromNow }}</div>..
`
})
I found a workaround:
Based on what I wrote in the question, in [my project folder]\main.html:
System.config({
defaultJSExtensions: true,
map: {
...
}
});
I substitued: 'moment':'node_modules/moment/moment' with 'moment':'node_modules/moment/min/moment-with-locales.min.js'
In my Pipe file I just kept: import * as moment from 'moment';at the beginning of the file and it works: all languages are available.

How to run intern to test a dojo application running with node.js ?

I'm trying to use intern to test a dojo application running under node.js
My intern.js configuration file is something like:
define({
loader: {
packages: [
{ name: 'elenajs', location: 'lib' },
{ name: 'tests', location: 'tests' }
],
map: {
'elenajs': { dojo: 'node_modules/dojo' },
'tests': { dojo: 'node_modules/dojo' }
}
},
suites: [ 'tests/all' ]});
When I try to run a test with node node_modules/intern/client.js config=tests/intern, I get this error:
Error: node plugin failed to load because environment is not Node.js.
Normally I would have configured dojo with something like
dojoConfig = {
...
hasCache: {
"host-node": 1, // Ensure we "force" the loader into Node.js mode
"dom": 0 // Ensure that none of the code assumes we have a DOM
},
...
};
How can I solve this with intern?
The issue that you are experiencing is caused by the fact that there is code in Dojo that relies on certain has-rules being set from the Dojo loader, but this doesn’t happen because the Dojo loader isn’t in use. There are a couple of potential workarounds:
Since Intern’s loader sets some of the same rules, you can load intern/node_modules/dojo/has and run has.add('dojo-has-api', true) before anything else tries to load your dojo/has module. This should cause Dojo to use the has.js implementation from Intern’s loader (and adopt all the rules it already has set, which currently includes host-node). The best place to do this is going to be the Intern configuration file, which would end up being something like this:
define([ 'intern/dojo/has' ], function (has) {
has.add('dojo-has-api', true);
return {
// your existing Intern configuration object…
};
});
Before loading any modules that call has('host-node'), load dojo/has and intern/node_modules/dojo/has and call dojoHas.add('host-node', internHas('host-node')) (or I guess you can just hard code it :)). This would require a loader plugin to be used in place of your suites array:
// tests/preload.js
define({
load: function (id, require, callback) {
require([ 'dojo/has' ], function (has) {
has.add('host-node', true);
require(id.split(/\s*,\s*/), callback);
}
}
});
And then your suites would change to suites: [ 'tests/preload!tests/all,tests/foo,tests/bar' ].
Any other has-rules that Dojo code relies on that are set by the Dojo loader will need to be set yourself. Any other rules that Dojo adds from other parts of itself will work correctly.

require.js shim, export myOwnGlobal name

I'm not sure about the use of "exports" on shim config, following the example on the requireJS API, I can use Backbone (B in capital letter) to export it to a global scope.
This means that it will be a window object property.
But I realized that I'm forced to use that name, and I can't export it by other reference name, ie: "MyGlobalBackbone"
require.config({
paths: {
backboneAlias:'backbone'
},
shim : {
backboneAlias : {
deps : [ 'underscore', 'jquery-1.9.1' ],
exports : 'MyGlobalBackbone'
}
}
});
require(['backboneAlias'],function(backboneAsAliasDependency){
console.log(backboneAsAliasDependency);//Loaded Ok
console.log(MyGlobalBackbone); //Uncaught ReferenceError: MyGlobalBackbone is not defined
});
This code only works if I use "Backbone" instead of "MyGlobalBackbone"...
Actually you got it the other way around: shimming doesn't export a variable to global scope, it imports it FROM the global scope. The name ("Backbone") was set by Backbone's author, and this is the part you're explaining to RequireJS in shim config element.
See it in the API:
http://requirejs.org/docs/api.html#config-shim
Look at this sentence:
//Once loaded, use the global 'Backbone' as the
//module value.
Let's see it in that way, you will understand it:
//Once loaded, use a global variable 'Backbone' that defined by the backbone vendor as the
//module value.
You should use map to make an alias.
require.config({
paths: {
...
},
shim : {
...
},
map: {
'*': {
'MyGlobalBackbone': 'Backbone'
}
}
});
This will allow you to use MyGlobalBackbone instead of Backbone for all (*) modules.

Having trouble defining global var in require.js

I'm trying to define a global object that i can reference across all of my modules. however, in the modules, i am unable to reference my path, and it's saying that "g" does not exist.
In main1.js, i have this:
requirejs.config({
paths: {
Underscore: 'lib/underscore/1.3.3/underscore.min',
Backbone: 'lib/backbone/0.9.2/backbone.min',
Globals: 'lib/backbone/ globalVars'
}
});
require([ 'views/pages', 'views/filters'], function(allPages, filters) {
filters.render();
allPages.render();
});
inside globalVars.js, i have this:
(function() {
var Globals = {
isDemo: false
}
console.log('in globalvars') // this shows in my console
}).call(this);
and finally, inside of view/pages.js, i have this:
define([
'Globals',
'Underscore',
'Backbone'
], function(g, _, Backbone){
console.log(g.isDemo) //<-- returns "TypeError: g is undefined"
If i use a define inside my main1.js like this:
define( 'Globals', function() {
return {
isDemo: true
}
})
it works just fine. I haven't had much luck with trying to figure out why this is not working. I'd like to be able to just include a path to the globalVars rather than boilerplate pasting a define block in each and every module that needs it, since changing isDemo to false would require updating many other module pages (main2.js, main3.js, etc) as well. thanks!
Well, to start with, your globalVars.js is not in the module pattern, so requirejs doesn't know what you're trying to register as the module. If you change that file to use the pattern, like the define you added to main1.js, you should be all set. Is there a reason you aren't defining it as a module?

Resources