weird `Method cannot be called on possibly null / undefined value` - flowtype

The following narrowed-down code:
// #flow
'use strict';
import assert from 'assert';
class Node<V, E> {
value: V;
children: ?Map<E, Node<V,E>>;
constructor(value: V) {
this.value = value;
this.children = null;
}
}
function accessChildren(tree: Node<number, string>): void {
if (tree.children!=null) {
assert(true); // if you comment this line Flow is ok
tree.children.forEach( (v,k)=>{});
} else {
}
}
… fails Flow type checking with the following message:
$ npm run flow
> simple-babel-serverside-node-only-archetype#1.0.0 flow /home/blah/blah/blah
> flow; test $? -eq 0 -o $? -eq 2
es6/foo.js:21
21: tree.children.forEach( (v,k)=>{});
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `forEach`. Method cannot be called on possibly null value
21: tree.children.forEach( (v,k)=>{});
^^^^^^^^^^^^^ null
es6/foo.js:21
21: tree.children.forEach( (v,k)=>{});
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `forEach`. Method cannot be called on possibly undefined value
21: tree.children.forEach( (v,k)=>{});
^^^^^^^^^^^^^ undefined
Found 2 errors
If the line reading: assert(true) is commented out, flow is satisfied !
What gives?
PS: In case anyone wonders, my .flowconfig, .babelrc and package.json files are nondescript:
.flowconfig
$ cat .flowconfig
[options]
esproposal.class_static_fields=enable
.babelrc
$ cat .babelrc
{
"presets": ["es2015"],
"plugins": ["transform-object-rest-spread", "transform-flow-strip-types", "transform-class-properties"]
}
package.json
$ cat package.json
{
"name": "simple-babel-serverside-node-only-archetype",
"version": "1.0.0",
"description": "",
"main": [
"index.js"
],
"scripts": {
"build": "babel es6 --out-dir es5 --source-maps",
"build-watch": "babel es6 --out-dir es5 --source-maps --watch",
"start": "node es5/index.js",
"flow": "flow; test $? -eq 0 -o $? -eq 2"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.6.5",
"babel-core": "^6.7.4",
"babel-plugin-transform-class-properties": "^6.10.2",
"babel-plugin-transform-flow-strip-types": "^6.8.0",
"babel-polyfill": "^6.7.4",
"babel-preset-es2015": "^6.9.0",
"babel-runtime": "^6.6.1",
"flow-bin": "^0.27.0"
},
"dependencies": {
"babel-plugin-transform-object-rest-spread": "^6.8.0",
"babel-polyfill": "^6.7.4",
"source-map-support": "^0.4.0"
}
}

Your case is described here.
Flow cannot know, that assert doesn't change the tree.
Add the following lines to your code and run it – you will have runtime error, because assert function will set tree.children to null when called.
const root = new Node(1);
const child = new Node(2);
root.children = new Map([['child', child]]);
assert = () => root.children = null;
accessChildren(root);
Yes, it is pretty weird code, but Flow doesn't know, you will not to write it.

Others have pointed to the right explanation. Fortunately this works:
// #flow
'use strict';
import assert from 'assert';
class Node<V, E> {
value: V;
children: ?Map<E, Node<V, E>>;
constructor(value: V) {
this.value = value;
this.children = null;
}
}
function accessChildren(tree: Node<number, string>): void {
const children = tree.children; // save possibly mutable reference to local
if (children != null) {
assert(true); // if you comment this line Flow is ok
children.forEach((v, k) => {});
} else {
}
}
Also, in the future Flow will have read-only properties and by declaring children as a read-only property in the class, Flow should be able to preserve type check the original code.

Related

How do I get the path of a downloaded deno module?

Given a downloaded deno module like:
import x from "https://deno.land/x/reggi#0.0.1/calendar/denodb/fresh/island.ts"
How can I find where this specific module is located on the host machine?
➜ deno.land cd x
cd: no such file or directory: x
➜ deno.land pwd
/Users/thomasreggi/Library/Caches/deno/deps/https/deno.land
There seems to be no "x" dir here πŸ‘†
Where is the file stored locally?
How can I create the local file path from url via code / api?
Notes:
The Linking to Third Party Code section in the manual will be a great entrypoint reference on this topic.
The below information was written when Deno's latest release was version 1.25.1.
Deno has a dependency inspector tool:
deno info [URL] will inspect an ES module and all of its dependencies.
This tool prints all of the dependencies of a module to stdout, and the output includes the module's remote origin as well as its cached path on your local storage device.
It also has a (currently unstable) argument --json which will print the dependencies in JSON format (which is easier to parse programmatically if that's your goal).
You can create a subprocess in your program that uses the above command and argument, then parse its output in order to programmatically determine a module's cached location. (Also note that the subprocess API is changing and the currently unstable APIs Deno.spawn and Deno.spawnChild will replace the current one.)
Here's some example output from a module with fewer dependencies than the one in your question:
% deno info https://deno.land/std#0.154.0/testing/asserts.ts
local: /Users/deno/.deno/deps/https/deno.land/ce1734220fb8c205d2de1b52a59b20401c59d0707abcc9765dbeb60c25483df9
type: TypeScript
dependencies: 3 unique (total 46.65KB)
https://deno.land/std#0.154.0/testing/asserts.ts (23.22KB)
β”œβ”€β”€ https://deno.land/std#0.154.0/fmt/colors.ts (11.62KB)
β”œβ”€β”¬ https://deno.land/std#0.154.0/testing/_diff.ts (11.11KB)
β”‚ └── https://deno.land/std#0.154.0/fmt/colors.ts *
└── https://deno.land/std#0.154.0/testing/_format.ts (705B)
Output when using the --json argument:
% deno info --json https://deno.land/std#0.154.0/testing/asserts.ts
{
"roots": [
"https://deno.land/std#0.154.0/testing/asserts.ts"
],
"modules": [
{
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/02d8068ecd90393c6bf5c8f69b02882b789681b5c638c210545b2d71e604b585",
"emit": null,
"map": null,
"size": 11904,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/fmt/colors.ts"
},
{
"dependencies": [
{
"specifier": "../fmt/colors.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/fmt/colors.ts",
"span": {
"start": {
"line": 11,
"character": 7
},
"end": {
"line": 11,
"character": 25
}
}
}
}
],
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/62cb97c1d18d022406d28b201c22805c58600e9a6d837b0fc4b71621ed21e30d",
"emit": null,
"map": null,
"size": 11380,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/testing/_diff.ts"
},
{
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/3f50b09108fe404c8274e994b417a0802863842e740c1d7ca43c119c0ee0f14b",
"emit": null,
"map": null,
"size": 705,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/testing/_format.ts"
},
{
"dependencies": [
{
"specifier": "../fmt/colors.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/fmt/colors.ts",
"span": {
"start": {
"line": 10,
"character": 32
},
"end": {
"line": 10,
"character": 50
}
}
}
},
{
"specifier": "./_diff.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/testing/_diff.ts",
"span": {
"start": {
"line": 11,
"character": 44
},
"end": {
"line": 11,
"character": 56
}
}
}
},
{
"specifier": "./_format.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/testing/_format.ts",
"span": {
"start": {
"line": 12,
"character": 23
},
"end": {
"line": 12,
"character": 37
}
}
}
}
],
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/ce1734220fb8c205d2de1b52a59b20401c59d0707abcc9765dbeb60c25483df9",
"emit": null,
"map": null,
"size": 23776,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/testing/asserts.ts"
}
],
"redirects": {}
}
However, if your ultimate goal is to cache modules (that come from a remote origin) to your local storage device and import them from that location rather than use Deno's built-in cache, I recommend using the built-in tool for that: deno vendor. From its manual page:
deno vendor <specifiers>... will download all remote dependencies of the specified modules into a local vendor folder.
Update: Here's an example script demonstrating the method I described above:
so-73596066.ts:
/// <reference lib="deno.unstable" />
import { assertExists } from "https://deno.land/std#0.154.0/testing/asserts.ts";
/*
`deno info --json` is unstable, and I didn't find any mention of schema for its
output in the docs, but here's a (conservative) partial type for the bits
that are relevant to this example, derived from looking at just a couple
of outputs from Deno v1.25.1:
*/
type ModuleInfo =
& Record<"kind" | "local" | "mediaType" | "specifier", string>
& Record<"emit" | "map", string | null>
& {
dependencies?: unknown[];
size: number;
};
type DependencyInspectorResult = {
modules: ModuleInfo[];
roots: string[];
};
/**
* Creates a formatted error message and allows for improved error handling by
* discriminating error instances
*/
class ProcessError extends Error {
override name = "ProcessError";
constructor(status: Deno.ChildStatus, stdErr?: string) {
let msg = `The process exited with status code ${status.code}`;
if (stdErr) msg += `. stderr:\n${stdErr}`;
super(msg);
}
}
/**
* Parses output from `deno info --json`. The resulting command will look like:
* `deno info --json [...denoInfoArgs] specifier`
* #param specifier local/remote path/URL
* #param denoInfoArgs optional, additional arguments to be used with `deno info --json`
*/
async function getCachedModuleInfo(
specifier: string | URL,
denoInfoArgs?: string[],
): Promise<ModuleInfo> {
const decoder = new TextDecoder();
const specifierStr = String(specifier);
const args = ["info", "--json"];
if (denoInfoArgs?.length) args.push(...denoInfoArgs);
args.push(specifierStr);
const { stderr, stdout, ...status } = await Deno.spawn("deno", { args });
if (!status.success) {
const stdErr = decoder.decode(stderr).trim();
throw new ProcessError(status, stdErr);
}
const result = JSON.parse(
decoder.decode(stdout),
) as DependencyInspectorResult;
const moduleInfo = result.modules.find((info) =>
info.specifier === specifierStr
);
assertExists(moduleInfo, "Module not found in output");
return moduleInfo;
}
/**
* `console.log` truncates long strings and deep object properties by default.
* This overrides that behavior.
*/
function print(value: unknown): void {
const inspectOpts: Deno.InspectOptions = {
colors: true,
depth: Infinity,
strAbbreviateSize: Infinity,
};
const formattedOutput = Deno.inspect(value, inspectOpts);
console.log(formattedOutput);
}
async function main() {
const moduleInfo = await getCachedModuleInfo(
"https://deno.land/std#0.154.0/testing/asserts.ts",
);
const { local, specifier } = moduleInfo;
print({ specifier, local });
}
if (import.meta.main) main();
% deno --version
deno 1.25.1 (release, x86_64-apple-darwin)
v8 10.6.194.5
typescript 4.7.4
% deno run --allow-run=deno --unstable so-73596066.ts
{
specifier: "https://deno.land/std#0.154.0/testing/asserts.ts",
local: "/Users/deno/.deno/deps/https/deno.land/ce1734220fb8c205d2de1b52a59b20401c59d0707abcc9765dbeb60c25483df9"
}

Spring Cloud Contract generates Pacts with empty body when the body is a list

I'm trying to generate pacts from spring cloud contracts as shown at the documentation. It works just find when the response body root is a json, however when I'm trying to generate a pact that return an array of jsons it generates an empty body. IΒ΄ve tried using groovy dsl with String format """[{}...]""" and using DslProperty [value()...]. Here are my contracts:
With string format
Contract.make {
description "should return a list of dummy object with dummy value. Generates pact with empty json"
request {
method GET()
url("/dummy")
}
response {
body("""[{"value": "Hi! I'm a dummy object ;)"}]""")
headers {
contentType applicationJson()
}
status 200
}}
With DslProperty
Contract.make {
description "should return a list of dummy object with dummy value. Generates pact with empty body list"
request {
method GET()
url("/dummy")
}
response {
body([value(value: "Hi! I'm a dummy object ;)")])
headers {
contentType applicationJson()
}
status 200
}}
And that's the file generate at target/pacts
{
"provider": {
"name": "Provider"
},
"consumer": {
"name": "Consumer"
},
"interactions": [
{
"description": "should return a list of dummy object with dummy value. Generates pact with empty body list",
"request": {
"method": "GET",
"path": "/dummy"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
],
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json.*"
}
],
"combine": "AND"
}
}
}
}
},
{
"description": "should return a list of dummy object with dummy value. Generates pact with empty json",
"request": {
"method": "GET",
"path": "/dummy"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json.*"
}
],
"combine": "AND"
}
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.23"
}
}}
I'm using the following versions
<spring-cloud.version>Hoxton.BUILD-SNAPSHOT</spring-cloud.version>
<spring-cloud-contract.version>2.0.1.RELEASE</spring-cloud-contract.version>
<pact-jvm-provider-maven.version>3.5.23</pact-jvm-provider-maven.version>
and that's my plugin configuration
<!-- SCC to pact see https://cloud.spring.io/spring-cloud-contract/reference/html/howto.html#how-to-generate-pact-from-scc-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>convert-dsl-to-pact</id>
<phase>process-test-classes</phase>
<configuration>
<classpathScope>test</classpathScope>
<mainClass>
org.springframework.cloud.contract.verifier.util.ToFileContractsTransformer
</mainClass>
<arguments>
<argument>
org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter
</argument>
<argument>${project.basedir}/target/pacts</argument>
<argument>
${project.basedir}/src/test/resources/contracts
</argument>
</arguments>
</configuration>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
While debugging throgh the plugin I've seen that what is happening respectively is:
- When declaring body as β€œβ€β€ [{...}] β€œβ€β€ Pact converter assumes that the body is an String instance so it goes through traverse method at org.springframework.cloud.contract.verifier.spec.pact.BodyConverter. And since it starts with [ it is not parsed.
org.springframework.cloud.contract.verifier.spec.pact.BodyConverter
private static DslPart traverse(Object value, DslPart parent, Closure dslPropertyValueExtractor) {
...
if (v instanceof String) {
v = v.trim()
if (v.startsWith("{") && v.endsWith("}")) {
try {
v = jsonSlurper.parseText(v as String)
}
catch (JsonException ex) { /*it wasn't a JSON string after all...*/
}
}
}
...
On the other hand, when going through the plugin code using the DslProperty I have an object like [DslProperty{clientValue=DslProperty}]. The first DslProperty is being extracted but since the content is another DslProperty and there’s no recursive extraction I end up with an empty body because v isn’t an instance of the Gstring, String, Number, Map, Collection. So I get an empty body again.
org.springframework.cloud.contract.verifier.spec.pact.BodyConverter
private static void processCollection(Collection values, PactDslJsonArray jsonArray, Closure dslPropertyValueExtractor) {
values.forEach({
Object v = it
if (v instanceof DslProperty) {
v = dslPropertyValueExtractor(v)
}
if (v instanceof GString) {
v = ContentUtils.extractValue(v, dslPropertyValueExtractor)
}
if (v == null) {
jsonArray.nullValue()
}
else if (v instanceof String) {
jsonArray.string(v)
}
else if (v instanceof Number) {
jsonArray.number(v)
}
else if (v instanceof Map) {
PactDslJsonBody current = jsonArray.object()
traverse(v, current, dslPropertyValueExtractor)
current.closeObject()
}
else if (v instanceof Collection) {
PactDslJsonArray current = jsonArray.array()
traverse(v, current, dslPropertyValueExtractor)
current.closeArray()
}
})
}
I've published an example at https://github.com/brjt23/contract-to-pact/tree/master in case more info about how I did build the project is required.
Is there something I'm doing wrong on defining my groovy contract files? I guess I missunderstood something about how the response body should be defined.
You need to create an array of groovy objects in your body like this:
body([
[value: "Object1"],
[value: "Object2"]
])
This way spring cloud contracts will generate the correct code needed for your contracts.

SyntaxError: Unexpected token { in Gruntfile.js

I don't manage to configure grunt. I have followed all the steps from Magento2 but I receive this syntax error.
grunt
Loading "Gruntfile.js" tasks...ERROR
>> SyntaxError: Unexpected token {
Warning: Task "default" not found. Use --force to continue.
Aborted due to warnings.
I have reinstalled both the grunt and the node.js, but it doesn't work.
Has anybody had the same problem?
Below you can see the Gruntfile.js ( that is original) posted.
Is it an error of this file or is there another problem?
Gruntfile.js
module.exports = function (grunt) {
'use strict';
var _ = require('underscore'),
path = require('path'),
filesRouter = require('./dev/tools/grunt/tools/files-router'),
configDir = './dev/tools/grunt/configs',
tasks = grunt.file.expand('./dev/tools/grunt/tasks/*'),
themes;
filesRouter.set('themes', 'dev/tools/grunt/configs/themes');
themes = filesRouter.get('themes');
tasks = _.map(tasks, function(task){ return task.replace('.js', '') });
tasks.push('time-grunt');
tasks.forEach(function (task) {
require(task)(grunt);
});
require('load-grunt-config')(grunt, {
configPath: path.join(__dirname, configDir),
init: true,
jitGrunt: {
staticMappings: {
usebanner: 'grunt-banner'
}
}
});
_.each({
/**
* Assembling tasks.
* ToDo: define default tasks.
*/
default: function () {
grunt.log.subhead('I\'m default task and at the moment I\'m empty, sorry :/');
},
/**
* Production preparation task.
*/
prod: function (component) {
var tasks = [
'less',
'autoprefixer',
'cssmin',
'usebanner'
].map(function(task){
return task + ':' + component;
});
if (typeof component === 'undefined') {
grunt.log.subhead('Tip: Please make sure that u specify prod subtask. By default prod task do nothing');
} else {
grunt.task.run(tasks);
}
},
/**
* Refresh themes.
*/
refresh: function () {
var tasks = [
'clean',
'exec:all'
];
_.each(themes, function(theme, name) {
tasks.push('less:' + name);
});
grunt.task.run(tasks);
},
/**
* Documentation
*/
documentation: [
'replace:documentation',
'less:documentation',
'styledocco:documentation',
'usebanner:documentationCss',
'usebanner:documentationLess',
'usebanner:documentationHtml',
'clean:var',
'clean:pub'
],
'legacy-build': [
'mage-minify:legacy'
],
spec: function (theme) {
var runner = require('./dev/tests/js/jasmine/spec_runner');
runner.init(grunt, { theme: theme });
grunt.task.run(runner.getTasks());
}
}, function (task, name) {
grunt.registerTask(name, task);
});
};
Thanks in advance!
I was getting the same error.
I installed node using the following commands and error resolved.
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
sudo apt install nodejs
node -v
npm -v
Hope this helps!

Testing babel-preset-env using package.json

Noob question here. Trying to assure myself that babel-preset-env works.
I install babel-core and babel-preset-env:
yarn add --dev babel-core
yarn add --dev babel-preset-env
My package.json has:
"babel": {
"presets": [
[
"env",
{
"targets": {
"browsers": [
"IE >= 8"
]
}
}
]
]
},
I create a JS script to test:
fs.readFile('my.js', 'utf8', (err, data) => {
if (err) throw err;
let babel = require("babel-core");
let result = babel.transform(data).code;
});
I test with arrow functions in my.js:
new Promise((resolve, reject) => {
console.log('whatever');
});
No matter how I tweak targets.browsers, the arrow function does not get converted.
Tested this with babel-cli. Works.
It's clear that babel-core (the Javascript API of Babel) does not pick up anything from package.json.
Filed an issue here too: https://github.com/babel/babel/issues/7647
The JS API docs probably needs to be updated.
In order to use babel-preset-env in JS API:
const { execFile } = require('child_process');
const ug = require('uglify-es');
const { execFile } = require('child_process');
execFile('npx', ['babel', 'my.js'], (err, data, stderr) => {
if (err) throw err; // U get the idea
let result = ug.minify(data, { mangle: { toplevel: true } }).code;
});

stylelint - Where are there examples to create your own plugin?

I have gone to the stylelint website, github and downloaded locally via npm. The stylelint website advises that to create my own plugin I should use this format:
var myPluginRule = stylelint.createPlugin(ruleName, function(primaryOption, secondaryOptionObject) {
return function(postcssRoot, postcssResult) {
var validOptions = stylelint.utils.validateOptions(postcssResult, ruleName, { .. })
if (!validOptions) { return }
// ... some logic ...
stylelint.utils.report({ .. })
}
})
When I do a 'find' inside the npm folder for stylelint, I cannot find any examples that use this format. Can anyone advise a really good tutorial on creating your own plugin?
thanks
ok after playing around with it I have literally found a way.
1) prerequisites:
$ npm init
$ npm install gulp stylelint gulp-style-lint --save-dev
2) Create some scss files at ./scss/myfile.scss
body{background:red;}
3) create ./gulpfile.js
var gulp = require('gulp');
var gulpStylelint = require('gulp-stylelint');
gulp.task('stylelint',function(){
return gulp
.src('./scss/*.scss')
.pipe(
gulpStylelint({
reporters: [
{formatter: 'string', console: true}
]
})
);
})
4) create ./stylelintCustom/index.js
var stylelint = require("stylelint");
var ruleName = "steves/steve1";
var messages = stylelint.utils.ruleMessages(ruleName, {
rejected: 'steve rejects this',
});
module.exports = stylelint.createPlugin(ruleName, function(max, options) {
return function(root, result) {
// to access the variable for the whole of this file scss =
console.log(root.source.input);
// apply rules now...
// run reporter to output
}
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;
Being sure to name ruleName: "plugins/plugin". ie steves/steverule1 etc.
5) Be sure to create stylelintCustom/package.json
{
"name": "stylelint-steves-steve1",
"version": "0.0.1",
"main": "index.js",
"devDependencies": {
"stylelint": "~2.6.0"
},
"engines": {
"node": ">=0.10.0"
}
}
6) create: stylelint.rc
{
"plugins": [
"./stylelintCustom/index.js"
],
"rules": {
"steves/steve1": true
}
}
7) run gulp
$ gulp stylelint
Will output the scss, so you can run whatever js, regex you need here.
For reference of how the existing rules work in stylelint you can go to:
yourproject/node_modules/stylelint/dist/rules/*

Resources