diff --git a/packages/toolpad-app/src/appDom/index.ts b/packages/toolpad-app/src/appDom/index.ts index 96e250606a4..96d74527b63 100644 --- a/packages/toolpad-app/src/appDom/index.ts +++ b/packages/toolpad-app/src/appDom/index.ts @@ -11,7 +11,7 @@ import { } from '@mui/toolpad-core'; import invariant from 'invariant'; import { BoxProps, ThemeOptions as MuiThemeOptions } from '@mui/material'; -import { pascalCase, removeDiacritics, uncapitalize } from '@mui/toolpad-utils/strings'; +import { guessTitle, pascalCase, removeDiacritics, uncapitalize } from '@mui/toolpad-utils/strings'; import { mapProperties, mapValues, hasOwnProperty } from '@mui/toolpad-utils/collections'; import { ConnectionStatus } from '../types'; import { omit, update, updateOrCreate } from '../utils/immutability'; @@ -1206,8 +1206,12 @@ export function getRequiredEnvVars(dom: AppDom): Set { return new Set(allVars); } +export function getPageDisplayName(node: PageNode): string { + return node.attributes.displayName || guessTitle(node.name); +} + export function getPageTitle(node: PageNode): string { - return node.attributes.title || node.name; + return node.attributes.title || getPageDisplayName(node); } export function isCodePage(node: PageNode): boolean { diff --git a/packages/toolpad-app/src/runtime/ToolpadApp.tsx b/packages/toolpad-app/src/runtime/ToolpadApp.tsx index 13e173c0ff7..9cc83e04f45 100644 --- a/packages/toolpad-app/src/runtime/ToolpadApp.tsx +++ b/packages/toolpad-app/src/runtime/ToolpadApp.tsx @@ -1560,7 +1560,7 @@ function ToolpadAppLayout({ dom }: ToolpadAppLayoutProps) { () => pages.map((page) => ({ slug: page.name, - displayName: page.attributes.displayName ?? page.name, + displayName: appDom.getPageDisplayName(page), hasShell: page?.attributes.display !== 'standalone', })), [pages], diff --git a/packages/toolpad-app/src/toolpad/AppEditor/PageDisplayNameEditor.tsx b/packages/toolpad-app/src/toolpad/AppEditor/PageDisplayNameEditor.tsx index fea06c6465e..ecf5c596811 100644 --- a/packages/toolpad-app/src/toolpad/AppEditor/PageDisplayNameEditor.tsx +++ b/packages/toolpad-app/src/toolpad/AppEditor/PageDisplayNameEditor.tsx @@ -1,10 +1,11 @@ -import { TextField } from '@mui/material'; +import { IconButton, InputAdornment, TextField, Tooltip } from '@mui/material'; import * as React from 'react'; -import { AppDom, PageNode, setNodeNamespacedProp } from '../../appDom'; +import ResetIcon from '@mui/icons-material/RestartAlt'; +import * as appDom from '../../appDom'; import { useDomApi } from '../AppState'; interface PageDisplayNameEditorProps { - node: PageNode; + node: appDom.PageNode; } function validateInput(input: string) { @@ -16,9 +17,11 @@ function validateInput(input: string) { export default function PageDisplayNameEditor({ node }: PageDisplayNameEditorProps) { const domApi = useDomApi(); - const [pageDisplayNameInput, setPageDisplayNameInput] = React.useState( - node.attributes.displayName ?? node.name, - ); + + const pageDisplayName = React.useMemo(() => appDom.getPageDisplayName(node), [node]); + + const [pageDisplayNameInput, setPageDisplayNameInput] = React.useState(pageDisplayName); + React.useEffect(() => setPageDisplayNameInput(pageDisplayName), [pageDisplayName]); const handlePageDisplayNameChange = React.useCallback( (event: React.ChangeEvent) => setPageDisplayNameInput(event.target.value), @@ -26,11 +29,17 @@ export default function PageDisplayNameEditor({ node }: PageDisplayNameEditorPro ); const handleCommit = React.useCallback(() => { - domApi.update((dom: AppDom) => - setNodeNamespacedProp(dom, node, 'attributes', 'displayName', pageDisplayNameInput), + domApi.update((dom: appDom.AppDom) => + appDom.setNodeNamespacedProp(dom, node, 'attributes', 'displayName', pageDisplayNameInput), ); }, [node, pageDisplayNameInput, domApi]); + const handleReset = React.useCallback(() => { + domApi.update((dom: appDom.AppDom) => + appDom.setNodeNamespacedProp(dom, node, 'attributes', 'displayName', undefined), + ); + }, [node, domApi]); + const handleKeyPress = React.useCallback( (event: React.KeyboardEvent) => { if (event.code === 'Enter') { @@ -50,6 +59,18 @@ export default function PageDisplayNameEditor({ node }: PageDisplayNameEditorPro onKeyDown={handleKeyPress} error={!pageDisplayNameInput} helperText={validateInput(pageDisplayNameInput)} + InputProps={{ + endAdornment: + pageDisplayNameInput === node.attributes.displayName ? ( + + + + + + + + ) : null, + }} /> ); } diff --git a/packages/toolpad-app/src/toolpad/AppEditor/PagesExplorer/index.tsx b/packages/toolpad-app/src/toolpad/AppEditor/PagesExplorer/index.tsx index e2e651a64dd..ff42fa79b70 100644 --- a/packages/toolpad-app/src/toolpad/AppEditor/PagesExplorer/index.tsx +++ b/packages/toolpad-app/src/toolpad/AppEditor/PagesExplorer/index.tsx @@ -365,7 +365,8 @@ export default function PagesExplorer({ className }: PagesExplorerProps) { key={page.id} nodeId={page.id} toolpadNodeId={page.id} - labelText={page.name} + labelText={appDom.getPageDisplayName(page)} + title={page.name} onRenameNode={handleRenameNode} onDuplicateNode={handleDuplicateNode} onDeleteNode={handleDeletePage} diff --git a/packages/toolpad-app/src/toolpad/ToolpadHomeShell.tsx b/packages/toolpad-app/src/toolpad/ToolpadHomeShell.tsx deleted file mode 100644 index 81611ab82eb..00000000000 --- a/packages/toolpad-app/src/toolpad/ToolpadHomeShell.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - Box, - Divider, - List, - ListItem, - ListItemButton, - ListItemText, - Stack, - styled, -} from '@mui/material'; -import * as React from 'react'; -import { Link } from 'react-router-dom'; -import FlexFill from '../components/FlexFill'; -import { DOCUMENTATION_URL, REPOSITORY_URL } from '../constants'; -import ToolpadShell, { ToolpadShellProps } from './ToolpadShell'; - -const NavigationListItemButton = styled(ListItemButton)({ height: 56 }); - -export interface HomeShellProps extends ToolpadShellProps {} - -export default function HomeShell({ children, ...props }: HomeShellProps) { - return ( - - - - - - {/* @ts-expect-error https://github.com/mui/material-ui/issues/29875 */} - - - - - - - - - - {/* @ts-expect-error https://github.com/mui/material-ui/issues/29875 */} - - - - - - - {/* @ts-expect-error https://github.com/mui/material-ui/issues/29875 */} - - - - - - - - {children} - - - ); -} diff --git a/packages/toolpad-utils/package.json b/packages/toolpad-utils/package.json index 8224d311165..992e6660832 100644 --- a/packages/toolpad-utils/package.json +++ b/packages/toolpad-utils/package.json @@ -55,6 +55,7 @@ "invariant": "2.2.4", "prettier": "2.8.8", "react-is": "18.2.0", + "title": "3.5.3", "yaml": "2.3.4", "yaml-diff-patch": "2.0.0" }, @@ -62,6 +63,7 @@ "@types/invariant": "2.2.37", "@types/prettier": "2.7.3", "@types/react": "18.2.45", - "@types/react-is": "18.2.4" + "@types/react-is": "18.2.4", + "@types/title": "3.4.3" } } diff --git a/packages/toolpad-utils/src/strings.spec.ts b/packages/toolpad-utils/src/strings.spec.ts index b5d0d5fbe83..d5dde09d606 100644 --- a/packages/toolpad-utils/src/strings.spec.ts +++ b/packages/toolpad-utils/src/strings.spec.ts @@ -1,5 +1,12 @@ import { describe, test, expect } from 'vitest'; -import { findImports, capitalize, uncapitalize, pascalCase, camelCase } from './strings'; +import { + findImports, + capitalize, + uncapitalize, + pascalCase, + camelCase, + guessTitle, +} from './strings'; describe('findImports', () => { test('finds all imports', () => { @@ -87,3 +94,19 @@ describe('camelCase', () => { expect(camelCase(...got)).toEqual(expected); }); }); + +describe('guessTitle', () => { + test.each([ + ['camelCaseExample', 'Camel Case Example'], + ['snake_case_example', 'Snake Case Example'], + ['kebab-case-example', 'Kebab Case Example'], + ['ACRONYMExample', 'Acronym Example'], + ['helloACRONYMExample', 'Hello Acronym Example'], + ['HelloACRONYMExample', 'Hello Acronym Example'], + ['example123', 'Example 123'], + ['example123Wat', 'Example 123 Wat'], + ['example123more456', 'Example 123 More 456'], + ])('should split %p into %p', (got, expected) => { + expect(guessTitle(got)).toEqual(expected); + }); +}); diff --git a/packages/toolpad-utils/src/strings.ts b/packages/toolpad-utils/src/strings.ts index 1b5e02e439e..7b90460600d 100644 --- a/packages/toolpad-utils/src/strings.ts +++ b/packages/toolpad-utils/src/strings.ts @@ -1,3 +1,5 @@ +import title from 'title'; + /** * Makes the first letter of [str] uppercase. * Not locale aware. @@ -171,3 +173,17 @@ export function indent(text: string, length = 2): string { export function isValidJsIdentifier(base: string): boolean { return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(base); } + +export function guessTitle(str: string): string { + // Replace snake_case with space + str = str.replace(/[_-]/g, ' '); + // Split camelCase + str = str.replace(/([a-z0-9])([A-Z])/g, '$1 $2'); + // Split acronyms + str = str.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2'); + // Split numbers + str = str.replace(/([a-zA-Z])(\d+)/g, '$1 $2'); + str = str.replace(/(\d+)([a-zA-Z])/g, '$1 $2'); + + return title(str); +} diff --git a/test/integration/duplication/index.spec.ts b/test/integration/duplication/index.spec.ts index ebb581600c3..c4a68d7ffea 100644 --- a/test/integration/duplication/index.spec.ts +++ b/test/integration/duplication/index.spec.ts @@ -19,7 +19,7 @@ test('duplication', async ({ page }) => { await editorModel.goto(); { - await editorModel.openPageExplorerMenu('page1'); + await editorModel.openPageExplorerMenu('Page 1'); const duplicateMenuItem = page.getByRole('menuitem', { name: 'Duplicate' }); await duplicateMenuItem.click(); @@ -28,12 +28,12 @@ test('duplication', async ({ page }) => { const button = editorModel.appCanvas.getByRole('button', { name: 'hello world' }); await expect(button).toBeVisible(); - await editorModel.openPageExplorerMenu('page2'); + await editorModel.openPageExplorerMenu('Page 2'); const deleteMenuItem = page.getByRole('menuitem', { name: 'Delete' }); await deleteMenuItem.click(); const deleteButton = editorModel.confirmationDialog.getByRole('button', { name: 'Delete' }); await deleteButton.click(); - await expect(editorModel.getExplorerItem('page2')).toBeHidden(); + await expect(editorModel.getExplorerItem('Page 2')).toBeHidden(); } }); diff --git a/test/integration/editor/new.spec.ts b/test/integration/editor/new.spec.ts index 156ff43d9f0..95fadfda6c3 100644 --- a/test/integration/editor/new.spec.ts +++ b/test/integration/editor/new.spec.ts @@ -64,7 +64,7 @@ test('can create/delete page', async ({ page, localApp }) => { await editorModel.createPage('someOtherPage'); - const pageMenuItem = editorModel.getExplorerItem('someOtherPage'); + const pageMenuItem = editorModel.getExplorerItem('Some Other Page'); const pageFolder = path.resolve(localApp.dir, './toolpad/pages/someOtherPage'); const pageFile = path.resolve(pageFolder, './page.yml'); diff --git a/test/integration/pages/index.spec.ts b/test/integration/pages/index.spec.ts index 699d83fbc26..87bbcda482c 100644 --- a/test/integration/pages/index.spec.ts +++ b/test/integration/pages/index.spec.ts @@ -56,7 +56,7 @@ test('can rename page', async ({ page, localApp }) => { const oldPageFolder = path.resolve(localApp.dir, './toolpad/pages/page2'); await expect.poll(async () => folderExists(oldPageFolder)).toBe(true); - await editorModel.explorer.getByText('page2').dblclick(); + await editorModel.explorer.getByText('Page 2').dblclick(); await page.keyboard.type('renamedpage'); await page.keyboard.press('Enter'); diff --git a/test/integration/undo-redo/multiple-pages.spec.ts b/test/integration/undo-redo/multiple-pages.spec.ts index 2061ea6f382..038c0958461 100644 --- a/test/integration/undo-redo/multiple-pages.spec.ts +++ b/test/integration/undo-redo/multiple-pages.spec.ts @@ -25,7 +25,7 @@ test('test undo and redo through different pages', async ({ page }) => { }); await expect(pageButton1).toBeVisible(); - await editorModel.explorer.getByText('page2').click(); + await editorModel.explorer.getByText('Page 2').click(); const pageButton2 = editorModel.appCanvas.getByRole('button', { name: 'page2Button', diff --git a/yarn.lock b/yarn.lock index 26bd28c143a..1a00d9f9a4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2547,6 +2547,11 @@ dependencies: "@types/node" "*" +"@types/title@3.4.3": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@types/title/-/title-3.4.3.tgz#685f2f18f19d746e70a817ca4dac26c1dd217668" + integrity sha512-mjupLOb4kwUuoUFokkacy/VMRVBH2qtqZ5AX7K7iha6+iKIkX80n/Y4EoNVEVRmer8dYJU/ry+fppUaDFVQh7Q== + "@types/uglify-js@*": version "3.17.4" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.4.tgz#3c70021f08023e5a760ce133d22966f200e1d31c" @@ -3142,7 +3147,7 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== -ansi-styles@^3.2.1: +ansi-styles@^3.1.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -3184,6 +3189,11 @@ anymatch@^3.0.0, anymatch@~3.1.2: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +arch@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + archiver-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" @@ -3262,6 +3272,11 @@ are-we-there-yet@^3.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +arg@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-1.0.0.tgz#444d885a4e25b121640b55155ef7cd03975d6050" + integrity sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw== + arg@5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" @@ -4008,6 +4023,15 @@ chainsaw@~0.1.0: dependencies: traverse ">=0.3.0 <0.4" +chalk@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" + integrity sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q== + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + chalk@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -4157,6 +4181,14 @@ clipboard-copy@4.0.1: resolved "https://registry.yarnpkg.com/clipboard-copy/-/clipboard-copy-4.0.1.tgz#326ef9726d4ffe72d9a82a7bbe19379de692017d" integrity sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng== +clipboardy@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.2.tgz#2ce320b9ed9be1514f79878b53ff9765420903e2" + integrity sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw== + dependencies: + arch "^2.1.0" + execa "^0.8.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -4660,6 +4692,15 @@ cross-spawn@^4.0.0: lru-cache "^4.0.1" which "^1.2.9" +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -5863,6 +5904,19 @@ execa@8.0.1, execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + integrity sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA== + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -6413,6 +6467,11 @@ get-stream@6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== + get-stream@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -6776,6 +6835,11 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -7480,6 +7544,11 @@ is-stream@2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -9284,6 +9353,13 @@ npm-registry-fetch@^16.0.0: npm-package-arg "^11.0.0" proc-log "^3.0.0" +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== + dependencies: + path-key "^2.0.0" + npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -9866,6 +9942,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -11276,6 +11357,13 @@ sharp@^0.32.5: tar-fs "^3.0.4" tunnel-agent "^0.6.0" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -11283,6 +11371,11 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -11307,7 +11400,7 @@ siginfo@^2.0.0: resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== -signal-exit@3.0.7, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -11750,6 +11843,11 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -11864,6 +11962,13 @@ superjson@2.0.0: dependencies: copy-anything "^3.0.2" +supports-color@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + integrity sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw== + dependencies: + has-flag "^2.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12086,6 +12191,21 @@ tinyspy@^2.2.0: resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce" integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg== +title@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/title/-/title-3.5.3.tgz#b338d701a3d949db6b49b2c86f409f9c2f36cd91" + integrity sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q== + dependencies: + arg "1.0.0" + chalk "2.3.0" + clipboardy "1.2.2" + titleize "1.0.0" + +titleize@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.0.tgz#7d350722061830ba6617631e0cfd3ea08398d95a" + integrity sha512-TARUb7z1pGvlLxgPk++7wJ6aycXF3GJ0sNSBTAsTuJrQG5QuZlkUQP+zl+nbjAh4gMX9yDw9ZYklMd7vAfJKEw== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"