diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 65d4fa84d..e2b00c835 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -21,10 +21,13 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: lts/*
- - name: Install
- run: npm install --no-package-lock --legacy-peer-deps --force
+ - name: Setup PNPM
+ uses: pnpm/action-setup@v2
+ with:
+ version: latest
+ run_install: true
- name: Test
- run: npm test
+ run: pnpm test && node scripts/esm-tests.mjs
next:
if: github.ref != 'refs/heads/master'
runs-on: ubuntu-latest
@@ -37,10 +40,15 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: lts/*
+ - name: Setup PNPM
+ uses: pnpm/action-setup@v2
+ with:
+ version: latest
+ run_install: false
- name: Lint
run: |
- npm install react react-dom next --no-package-lock --legacy-peer-deps
- npx next lint
+ pnpm install react react-dom next
+ ./node_modules/.bin/next lint
urlint:
if: github.ref != 'refs/heads/master'
@@ -54,7 +62,12 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: lts/*
+ - name: Setup PNPM
+ uses: pnpm/action-setup@v2
+ with:
+ version: latest
+ run_install: false
- name: Lint
run: |
- npm install urlint --no-package-lock --legacy-peer-deps
- npx urlint https://microlink.io/sitemap.xml
+ pnpm install urlint
+ ./node_modules/.bin/urlint https://microlink.io/sitemap.xml
diff --git a/package.json b/package.json
index 5292dd47c..bb37f64a6 100644
--- a/package.json
+++ b/package.json
@@ -138,7 +138,7 @@
"postcss": "~8.4.27",
"postcss-focus": "~7.0.0",
"prepend-http": "~4.0.0",
- "prettier": "~2.8.8",
+ "prettier": "~3.0.0",
"react": "18",
"react-codecopy": "~5.0.1",
"react-confetti": "~6.1.0",
@@ -182,10 +182,11 @@
"simple-git-hooks": "latest",
"standard": "latest",
"standard-markdown": "latest",
- "standard-version": "latest"
+ "standard-version": "latest",
+ "zx": "latest"
},
"engines": {
- "node": ">= 14"
+ "node": ">= 18"
},
"files": [
"data",
diff --git a/scripts/esm-tests.mjs b/scripts/esm-tests.mjs
new file mode 100644
index 000000000..0642c2fb9
--- /dev/null
+++ b/scripts/esm-tests.mjs
@@ -0,0 +1,21 @@
+import { $ } from 'zx'
+import { dirname, resolve } from 'path'
+import { fileURLToPath } from 'url'
+import { readFile, writeFile } from 'fs/promises'
+
+const __dirname = dirname(fileURLToPath(import.meta.url))
+
+const pkgPath = resolve(__dirname, '../package.json')
+
+const pkg = JSON.parse(await readFile(pkgPath))
+pkg.type = 'module'
+
+await writeFile(pkgPath, JSON.stringify(pkg, null, 2))
+
+try {
+ await $`pnpm install ava@latest`
+ await $`./node_modules/.bin/ava test/**/*.mjs`
+} finally {
+ delete pkg.type
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2))
+}
diff --git a/src/components/elements/CodeEditor/CodeEditor.js b/src/components/elements/CodeEditor/CodeEditor.js
index e92ede06b..2a03b975c 100644
--- a/src/components/elements/CodeEditor/CodeEditor.js
+++ b/src/components/elements/CodeEditor/CodeEditor.js
@@ -1,12 +1,10 @@
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { hash, prettier, getLines, template } from 'helpers'
import { hideScrollbar, wordBreak } from 'helpers/style'
-import React, { useState } from 'react'
-import identity from 'lodash/identity'
+import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import { cx, radii } from 'theme'
import range from 'lodash/range'
-import get from 'dlv'
import codeTheme from './theme'
import Runkit from '../Runkit/Runkit'
@@ -83,22 +81,37 @@ const CodeEditor = ({
title,
...props
}) => {
- const [isLoaded, setIsLoaded] = useState(false)
const className = getClassName(props)
const highlightLines = getLines(className)
const language = toAlias(getLanguage(className, props))
- const pretty = props.prettier ? get(prettier, language, identity) : identity
- const text = pretty(template(children)).trim()
const id = `codeditor-${hash(children)}-${theme}`
- const isInteractive = Runkit.isSupported({ language, text })
+
+ const [content, setContent] = useState({
+ text: template(children).trim(),
+ isPretty: false
+ })
+
+ const isInteractive =
+ runkitProps !== false &&
+ Runkit.isSupported({ language, text: content.text })
+
+ const [isLoaded, setIsLoaded] = useState(!isInteractive)
+
+ useEffect(() => {
+ async function asyncPretty () {
+ const prettyText = await prettier[language](content.text)
+ setContent({ text: prettyText.trim(), isPretty: true })
+ }
+ asyncPretty()
+ }, [])
const TerminalComponent = (
@@ -111,13 +124,13 @@ const CodeEditor = ({
style={{}}
wrapLines
>
- {text}
+ {content.text}
)
- if (!isInteractive) return TerminalComponent
+ if (!isInteractive || !content.isPretty) return TerminalComponent
return (
{
setIsLoaded(true)
element.style['padding-top'] = '4px'
@@ -138,7 +151,7 @@ const CodeEditor = ({
CodeEditor.defaultProps = {
blinkCursor: false,
- prettier: true,
+ interactive: {},
showLineNumbers: false,
theme: 'light',
width: TERMINAL_WIDTH
diff --git a/src/components/elements/CodeEditor/CodeEditor.stories.js b/src/components/elements/CodeEditor/CodeEditor.stories.js
index dcd8dd5c3..66aa3e0c7 100644
--- a/src/components/elements/CodeEditor/CodeEditor.stories.js
+++ b/src/components/elements/CodeEditor/CodeEditor.stories.js
@@ -1,4 +1,4 @@
-import { Text, Box, CodeEditor } from 'components/elements'
+import { Text, Box, CodeEditor, Toggle } from 'components/elements'
import { storiesOf } from '@storybook/react'
import { Story } from 'story'
import React from 'react'
@@ -120,6 +120,12 @@ const jsonCode = JSON.stringify(
storiesOf('Elements', module).add('CodeEditor', () => (
+
+
+ {['Interactive', 'Non Interactive']}
+
+
+
{""}
diff --git a/src/components/elements/Toggle/Toggle.js b/src/components/elements/Toggle/Toggle.js
index 93da81c70..9e54c8383 100644
--- a/src/components/elements/Toggle/Toggle.js
+++ b/src/components/elements/Toggle/Toggle.js
@@ -43,12 +43,10 @@ function Toggle ({ onChange, children, defaultValue }) {
key={value}
borderRight={!isLast ? 1 : null}
borderColor={!isLast ? 'black05' : null}
+ pl={3}
+ pr={3}
>
-
+
(
-
- defaultValue={'MQL'}
+
+ {['SDK', 'MQL', 'API']}
)
`
diff --git a/src/components/hook/index.js b/src/components/hook/index.js
index 4cf84a18f..fd61e0bbe 100644
--- a/src/components/hook/index.js
+++ b/src/components/hook/index.js
@@ -12,7 +12,6 @@ export * from './use-fingerprint'
export * from './use-healthcheck'
export * from './use-hover'
export * from './use-local-storage'
-export * from './use-mounted-ref'
export * from './use-oss'
export * from './use-previous'
export * from './use-query-state'
diff --git a/src/components/hook/use-mounted-ref.js b/src/components/hook/use-mounted-ref.js
deleted file mode 100644
index e6cc1bb16..000000000
--- a/src/components/hook/use-mounted-ref.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { useRef, useEffect } from 'react'
-
-export const useMountedRef = () => {
- const isMounted = useRef(false)
-
- useEffect(() => {
- isMounted.current = true
- return () => (isMounted.current = false)
- }, [])
-
- return isMounted
-}
diff --git a/src/helpers/mql-code.js b/src/helpers/mql-code.js
index b4bda9e58..92161013b 100644
--- a/src/helpers/mql-code.js
+++ b/src/helpers/mql-code.js
@@ -19,9 +19,7 @@ const stringify = input => {
}
stringify.python = input =>
- stringify(input)
- .replaceAll('true', 'True')
- .replaceAll('false', 'False')
+ stringify(input).replaceAll('true', 'True').replaceAll('false', 'False')
const endpoint = ({ endpoint, headers } = {}) => {
const apiKey = headers && headers['x-api-key']
@@ -45,8 +43,8 @@ const stringProps = (props = {}) => {
Array.isArray(props[key])
? stringify(props[key])
: typeof props[key] === 'object'
- ? `{${stringProps(props[key])}}`
- : stringify(props[key])
+ ? `{${stringProps(props[key])}}`
+ : stringify(props[key])
}${keys.length === 1 || keys.length - 1 === index ? '' : ', '}`,
''
)
diff --git a/src/helpers/prettier.js b/src/helpers/prettier.js
index ba9be8248..e95c51a97 100644
--- a/src/helpers/prettier.js
+++ b/src/helpers/prettier.js
@@ -1,8 +1,6 @@
-import prettierStandalone from 'prettier/standalone'
-import prettierParserHtml from 'prettier/parser-html'
-import prettierParserGraphql from 'prettier/parser-graphql'
-import prettierParserMarkdown from 'prettier/parser-markdown'
-import parserBabel from 'prettier/parser-babel'
+import { format } from 'prettier/standalone'
+import estree from 'prettier/plugins/estree'
+import babel from 'prettier/plugins/babel'
/**
* https://prettier.io/docs/en/options.html
@@ -19,50 +17,49 @@ const PRETTIER_CONFIG = {
const JS_OPTS = {
parser: 'babel',
- plugins: [parserBabel]
+ plugins: [babel, estree]
}
const JSON_OPTS = {
parser: 'json',
- plugins: [parserBabel]
+ plugins: [babel, estree]
}
-const HTML_OPTS = {
- parser: 'html',
- plugins: [prettierParserHtml]
-}
-
-const GRAPHQL_OPTS = {
- parser: 'graphql',
- plugins: [prettierParserGraphql]
-}
-
-const MARKDOWN_OPTS = {
- parser: 'markdown',
- plugins: [prettierParserMarkdown]
-}
-
-export const serializeFmt = (props, { quotes = true } = {}) => {
+/**
+ * It turns an JS object:
+ *
+ * { foo: 'bar', apiKey: true }
+ *
+ * into fmt representation:
+ *
+ * `foo='bar' apiKey`
+ *
+ */
+export const serializeFmt = props => {
return Object.keys(props).reduce((acc, rawKey) => {
const rawValue = props[rawKey]
const key = rawValue === true ? rawKey : `${rawKey}=`
const value = (() => {
if (rawValue === true) return ''
-
if (Array.isArray(rawValue)) {
- return `{[${rawValue.map(
- value => `${quotes ? `'${value}'` : value}`
- )}]}`
+ return `{[${rawValue.map(serializePrimitive).join(', ')}]}`
}
-
- return `${quotes ? `'${rawValue}'` : rawValue}`
+ return serializeValue(rawValue)
})()
return `${acc}${key}${value} `
}, '')
}
-export const serializeObject = (props, { quotes = true } = {}) => {
+const serializePrimitive = value =>
+ typeof value === 'object'
+ ? `{ ${serializeObject(value)} }`
+ : serializeValue(value)
+
+const serializeValue = value =>
+ typeof value === 'number' ? `{${value}}` : `'${value}'`
+
+const serializeObject = (props, { quotes = true } = {}) => {
return Object.keys(props).reduce((acc, rawKey) => {
const rawValue = props[rawKey]
const key = rawValue === true ? rawKey : `${rawKey}: `
@@ -73,11 +70,10 @@ export const serializeObject = (props, { quotes = true } = {}) => {
}, '')
}
-const prettier = (code, opts) => {
+const prettier = async (code, opts) => {
try {
- return prettierStandalone
- .format(code, { ...PRETTIER_CONFIG, ...opts })
- .replace(';<', '<')
+ const pretty = await format(code, { ...PRETTIER_CONFIG, ...opts })
+ return pretty.replace(';<', '<')
} catch (error) {
if (error.name !== 'SyntaxError') console.error('[prettier]', error)
return code
@@ -86,10 +82,7 @@ const prettier = (code, opts) => {
prettier.jsx = prettier.js = (code, opts) =>
prettier(code, { ...JS_OPTS, ...opts })
-prettier.html = (code, opts) => prettier(code, { ...HTML_OPTS, ...opts })
-prettier.graphql = (code, opts) => prettier(code, { ...GRAPHQL_OPTS, ...opts })
-prettier.md = prettier.markdown = (code, opts) =>
- prettier(code, { ...MARKDOWN_OPTS, ...opts })
+
prettier.json = (code, opts) => prettier(code, { ...JSON_OPTS, ...opts })
export default prettier
diff --git a/test/helpers/get-lines.js b/test/helpers/get-lines.js
index cabde12d4..a97abf5ba 100644
--- a/test/helpers/get-lines.js
+++ b/test/helpers/get-lines.js
@@ -1,6 +1,6 @@
import test from 'ava'
-import getLines from '../../src/helpers/get-lines'
+import getLines from '../../src/helpers/get-lines.js'
const values = [
['', null],
diff --git a/test/helpers/prettier.js b/test/helpers/prettier.js
deleted file mode 100644
index 121a398b3..000000000
--- a/test/helpers/prettier.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import test from 'ava'
-
-import prettier from '../../src/helpers/prettier'
-
-test('js', t => {
- const code = `const mql = require('@microlink/mql')
-
- const { status, data } = await mql('https://geolocation.microlink.io', { apiKey: MyApiToken, proxy: 'https://myproxy:603f60f5@superproxy.cool:8001' })
-
- console.log(data)`
-
- const output = prettier.js(code)
-
- t.snapshot(output)
-})
diff --git a/test/helpers/prettier.mjs b/test/helpers/prettier.mjs
new file mode 100644
index 000000000..c44656e76
--- /dev/null
+++ b/test/helpers/prettier.mjs
@@ -0,0 +1,36 @@
+import test from 'ava'
+
+import prettier, { serializeFmt } from '../../src/helpers/prettier.js'
+
+test('js', async t => {
+ const code = `const mql = require('@microlink/mql')
+ const { status, data } = await mql('https://geolocation.microlink.io', { apiKey: MyApiToken, proxy: 'https://myproxy:603f60f5@superproxy.cool:8001' })
+ console.log(data)`
+
+ const output = await prettier.js(code)
+
+ t.snapshot(output)
+})
+
+test('json', async t => {
+ const code =
+ '{"title":"Wormholes Explained – Breaking Spacetime","description":"To support Kurzgesagt and learn more about Brilliant, go to https://www.brilliant.org/nutshell and sign up for free. The first 688 people that go to that lin...","lang":"en","author":"Kurzgesagt – In a Nutshell","publisher":"YouTube","image":{"url":"https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/img.youtube.com!vi!9P6rdqiybaw!maxresdefault.jpg.jpg","type":"jpg","size":120116,"height":720,"width":1280,"size_pretty":"120 kB","palette":["#C004F9","#EEEEA7","#25047C","#740296","#808018","#2C0494"],"background_color":"#EEEEA7","color":"#AC04DF","alternative_color":"#2C0494"},"audio":{"url":"https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/r3---sn-ab5sznly.googlevideo.com!videoplayback!c=WEB&clen=8935291&dur=552.054&ei=6gpAXv-3POHM8gTqtrm","type":"mp4","duration":552.054422,"size":8935291,"duration_pretty":"9m","size_pretty":"8.94 MB"},"url":"https://www.youtube.com/watch?v=9P6rdqiybaw","iframe":{"html":"","scripts":[]},"date":"2020-02-09T13:36:39.000Z","logo":{"url":"https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/logo.clearbit.com!youtube.com.png","type":"png","size":2421,"height":128,"width":128,"size_pretty":"2.42 kB","palette":["#FC0404","#FC8484","#830101","#970101","#950303","#970101"],"background_color":"#FC0404","color":"#320000","alternative_color":"#320101"},"video":{"url":"https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/r3---sn-ab5sznly.googlevideo.com!videoplayback!c=WEB&dur=552.054&ei=6gpAXv-3POHM8gTqtrmwBA&expire=15","type":"mp4","duration":552.007943,"size":54633895,"height":720,"width":1280,"duration_pretty":"9m","size_pretty":"54.6 MB"}}'
+
+ const output = await prettier.json(code)
+
+ t.snapshot(output)
+})
+
+test('serializeFmt', t => {
+ const output = serializeFmt({
+ type: 'email',
+ id: 'input',
+ placeholder: 'you@domain.com',
+ suggestions: [{ value: 'you@gmail.com' }, { value: 'you@hotmail.com' }],
+ width: '9rem',
+ fontSize: 1,
+ required: true
+ })
+
+ t.snapshot(output)
+})
diff --git a/test/helpers/snapshots/prettier.js.md b/test/helpers/snapshots/prettier.js.md
deleted file mode 100644
index 0a9c9001e..000000000
--- a/test/helpers/snapshots/prettier.js.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Snapshot report for `test/helpers/prettier.js`
-
-The actual snapshot is saved in `prettier.js.snap`.
-
-Generated by [AVA](https://avajs.dev).
-
-## js
-
-> Snapshot 1
-
- `const mql = require('@microlink/mql')␊
- ␊
- const { status, data } = await mql('https://geolocation.microlink.io', {␊
- apiKey: MyApiToken,␊
- proxy: 'https://myproxy:603f60f5@superproxy.cool:8001'␊
- })␊
- ␊
- console.log(data)␊
- `
diff --git a/test/helpers/snapshots/prettier.js.snap b/test/helpers/snapshots/prettier.js.snap
deleted file mode 100644
index 02050590d..000000000
Binary files a/test/helpers/snapshots/prettier.js.snap and /dev/null differ
diff --git a/test/helpers/snapshots/prettier.mjs.md b/test/helpers/snapshots/prettier.mjs.md
new file mode 100644
index 000000000..7a3f1b4df
--- /dev/null
+++ b/test/helpers/snapshots/prettier.mjs.md
@@ -0,0 +1,98 @@
+# Snapshot report for `test/helpers/prettier.mjs`
+
+The actual snapshot is saved in `prettier.mjs.snap`.
+
+Generated by [AVA](https://avajs.dev).
+
+## js
+
+> Snapshot 1
+
+ `const mql = require('@microlink/mql')␊
+ const { status, data } = await mql('https://geolocation.microlink.io', {␊
+ apiKey: MyApiToken,␊
+ proxy: 'https://myproxy:603f60f5@superproxy.cool:8001'␊
+ })␊
+ console.log(data)␊
+ `
+
+## json
+
+> Snapshot 1
+
+ `{␊
+ "title": "Wormholes Explained – Breaking Spacetime",␊
+ "description": "To support Kurzgesagt and learn more about Brilliant, go to https://www.brilliant.org/nutshell and sign up for free. The first 688 people that go to that lin...",␊
+ "lang": "en",␊
+ "author": "Kurzgesagt – In a Nutshell",␊
+ "publisher": "YouTube",␊
+ "image": {␊
+ "url": "https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/img.youtube.com!vi!9P6rdqiybaw!maxresdefault.jpg.jpg",␊
+ "type": "jpg",␊
+ "size": 120116,␊
+ "height": 720,␊
+ "width": 1280,␊
+ "size_pretty": "120 kB",␊
+ "palette": [␊
+ "#C004F9",␊
+ "#EEEEA7",␊
+ "#25047C",␊
+ "#740296",␊
+ "#808018",␊
+ "#2C0494"␊
+ ],␊
+ "background_color": "#EEEEA7",␊
+ "color": "#AC04DF",␊
+ "alternative_color": "#2C0494"␊
+ },␊
+ "audio": {␊
+ "url": "https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/r3---sn-ab5sznly.googlevideo.com!videoplayback!c=WEB&clen=8935291&dur=552.054&ei=6gpAXv-3POHM8gTqtrm",␊
+ "type": "mp4",␊
+ "duration": 552.054422,␊
+ "size": 8935291,␊
+ "duration_pretty": "9m",␊
+ "size_pretty": "8.94 MB"␊
+ },␊
+ "url": "https://www.youtube.com/watch?v=9P6rdqiybaw",␊
+ "iframe": {␊
+ "html": "",␊
+ "scripts": []␊
+ },␊
+ "date": "2020-02-09T13:36:39.000Z",␊
+ "logo": {␊
+ "url": "https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/logo.clearbit.com!youtube.com.png",␊
+ "type": "png",␊
+ "size": 2421,␊
+ "height": 128,␊
+ "width": 128,␊
+ "size_pretty": "2.42 kB",␊
+ "palette": [␊
+ "#FC0404",␊
+ "#FC8484",␊
+ "#830101",␊
+ "#970101",␊
+ "#950303",␊
+ "#970101"␊
+ ],␊
+ "background_color": "#FC0404",␊
+ "color": "#320000",␊
+ "alternative_color": "#320101"␊
+ },␊
+ "video": {␊
+ "url": "https://cdn.microlink.io/data/assets/youtube.com!watch!v=9P6rdqiybaw/r3---sn-ab5sznly.googlevideo.com!videoplayback!c=WEB&dur=552.054&ei=6gpAXv-3POHM8gTqtrmwBA&expire=15",␊
+ "type": "mp4",␊
+ "duration": 552.007943,␊
+ "size": 54633895,␊
+ "height": 720,␊
+ "width": 1280,␊
+ "duration_pretty": "9m",␊
+ "size_pretty": "54.6 MB"␊
+ }␊
+ }␊
+ `
+
+## serializeFmt
+
+> Snapshot 1
+
+ 'type=\'email\' id=\'input\' placeholder=\'you@domain.com\' suggestions={[{ value: \'you@gmail.com\' }, { value: \'you@hotmail.com\' }]} width=\'9rem\' fontSize={1} required '
diff --git a/test/helpers/snapshots/prettier.mjs.snap b/test/helpers/snapshots/prettier.mjs.snap
new file mode 100644
index 000000000..f2e0f2aaa
Binary files /dev/null and b/test/helpers/snapshots/prettier.mjs.snap differ