Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ module.exports = {
'@shopify/jsx-no-hardcoded-content': 'off',
},
},
{
files: ['polaris-migrator/src/**/*.{ts,tsx}'],
rules: {
'import/no-default-export': 'off',
},
},
{
files: ['polaris-migrator/src/**/tests/*.{ts,tsx}'],
rules: {
'import/no-default-export': 'off',
'import/no-extraneous-dependencies': 'off',
'@shopify/jsx-no-hardcoded-content': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
},
},
{
files: ['polaris-react/src/**/*.{ts,tsx}'],
extends: ['plugin:@shopify/typescript-type-checking'],
Expand Down
6 changes: 6 additions & 0 deletions .stylelintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ module.exports = {
'./stylelint-polaris/configs/internal',
],
},
{
files: ['polaris-migrator/**/tests/*.{css,scss}'],
rules: {
'declaration-property-value-disallowed-list': 'off',
},
},
],
ignoreFiles: [
'**/.next/**/*.{css,scss}',
Expand Down
1 change: 1 addition & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
'.',
// Note: The following projects use rootMode: 'upward' to inherit
// and merge with this root level config.
'./polaris-migrator',
'./polaris-tokens',
'./polaris-icons',
'./polaris-react',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"polaris-for-figma",
"polaris-for-vscode",
"polaris-icons",
"polaris-migrator",
"polaris-react",
"stylelint-polaris",
"polaris.shopify.com"
Expand All @@ -37,7 +38,7 @@
"gen-assets": "turbo run gen-assets --filter=polaris.shopify.com",
"preversion-packages": "turbo run preversion",
"version-packages": "yarn preversion-packages && changeset version",
"release": "turbo run build --filter=polaris.shopify.com^... && changeset publish",
"release": "turbo run build --filter='!polaris.shopify.com' && changeset publish",
"preversion": "echo \"Error: use @changsets/cli to version packages\" && exit 1"
},
"devDependencies": {
Expand Down
20 changes: 20 additions & 0 deletions polaris-migrator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# [Polaris Migrator](https://polaris.shopify.com/docs/advanced-features)

[![npm version](https://img.shields.io/npm/v/@shopify/polaris-migrator.svg?style=flat)](https://www.npmjs.com/package/@shopify/polaris-migrator)

Codemod transformations to help upgrade your Polaris codebase.

## Usage

```sh
npx @shopify/polaris-migrator <migration> <path>
```

- `migration` - name of migration, see available migrations on the docs site below.
- `path` - files or directory to perform migration
- `--dry` Do a dry-run, no code will be edited
- `--print` Prints the changed output for comparison

## Documentation

Visit [polaris.shopify.com/docs/advanced-features/migrations](https://polaris.shopify.com/docs/advanced-features/migrations) to view available migrations.
5 changes: 5 additions & 0 deletions polaris-migrator/bin/migrator-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node

const {run} = require('./../dist/cjs');

run();
8 changes: 8 additions & 0 deletions polaris-migrator/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
transform: {
'\\.(js|tsx?)$': [
'babel-jest',
{targets: 'current node', envName: 'test', rootMode: 'upward'},
],
},
};
54 changes: 54 additions & 0 deletions polaris-migrator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@shopify/polaris-migrator",
"version": "0.0.0",
"description": "Codemod transformations to help upgrade your Polaris codebase",
"license": "SEE LICENSE IN LICENSE.md",
"author": "Shopify <[email protected]>",
"homepage": "https://polaris.shopify.com",
"repository": "https://github.com/Shopify/polaris",
"bugs": {
"url": "https://github.com/Shopify/polaris/issues"
},
"publishConfig": {
"access": "public",
"@shopify:registry": "https://registry.npmjs.org"
},
"main": "dist/cjs/index.js",
"module": "dist/esm/index.mjs",
"types": "dist/types/index.d.ts",
"bin": "bin/migrator-cli.js",
"files": [
"bin",
"dist"
],
"scripts": {
"build": "run-s build:*",
"build:js": "rollup -c",
"build:types": "tsc -b",
"dev": "rollup -c -w",
"start": "./bin/migrator-cli.js",
"lint": "TIMING=1 eslint --cache .",
"test": "jest",
"clean": "rm -rf .turbo node_modules dist *.tsbuildinfo"
},
"dependencies": {
"chalk": "^4.1.0",
"globby": "11.0.1",
"is-git-clean": "^1.1.0",
"jscodeshift": "^0.13.1",
"meow": "^9.0.0",
"postcss": "^8.4.14",
"postcss-scss": "^4.0.4",
"postcss-value-parser": "^4.2.0"
},
"devDependencies": {
"@types/is-git-clean": "^1.1.0",
"@types/jscodeshift": "^0.11.5",
"@rollup/plugin-babel": "^5.3.1",
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@shopify/polaris": "^10.0.0",
"prettier": "^2.7.1"
}
}
57 changes: 57 additions & 0 deletions polaris-migrator/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as url from 'node:url';

import {babel} from '@rollup/plugin-babel';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import globby from 'globby';

const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json')));

const extensions = ['.js', '.jsx', '.ts', '.tsx'];

const migrationPaths = globby.sync('./src/migrations/*!(tests)/*.ts');

/** @type {import('rollup').RollupOptions} */
export default {
input: ['src/index.ts', ...migrationPaths],
output: [
{
format: /** @type {const} */ ('cjs'),
entryFileNames: '[name][assetExtname].js',
dir: path.dirname(pkg.main),
preserveModules: true,
exports: 'auto',
},
{
format: /** @type {const} */ ('esm'),
entryFileNames: '[name][assetExtname].mjs',
dir: path.dirname(pkg.module),
preserveModules: true,
},
],
plugins: [
// Allows node_modules resolution
nodeResolve({extensions, preferBuiltins: true}),
// Allow bundling cjs modules. Rollup doesn't understand cjs
commonjs(),
// Compile TypeScript/JavaScript files
babel({
extensions,
rootMode: 'upward',
include: ['src/**/*'],
babelHelpers: 'bundled',
envName: 'production',
targets: 'node 14.13',
}),
json({compact: true}),
],
external: [
...Object.keys(pkg.dependencies ?? {}),
...Object.keys(pkg.peerDependencies ?? {}),
'jscodeshift/src/Runner',
],
};
119 changes: 119 additions & 0 deletions polaris-migrator/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import fs from 'node:fs';
import path from 'node:path';

// @ts-expect-error TS can't resolve the type of this import
import * as jscodeshift from 'jscodeshift/src/Runner';
import chalk from 'chalk';
import isGitClean from 'is-git-clean';
import globby from 'globby';
import meow from 'meow';

const cli = meow({
description: 'Code migrations for updating Polaris apps.',
help: `
Usage
$ npx @shopify/polaris-migrator <migration> <path> <...options>
migration One of the choices from https://polaris.shopify.com/docs/advanced-features/migrations
path Files or directory to transform. Can be a glob like src/**.scss
Options
--force Bypass Git safety checks and forcibly run migrations
--dry Dry run (no changes are made to files)
--print Print transformed files to your terminal
`,
flags: {
force: {
type: 'boolean',
},
dry: {
type: 'boolean',
},
print: {
type: 'boolean',
},
},
});

export function checkGitStatus(force?: boolean) {
let clean = false;
let errorMessage = 'Unable to determine if git directory is clean';
try {
clean = isGitClean.sync(process.cwd());
errorMessage = 'Git directory is not clean';
} catch (err: any) {
if (err && err.stderr && err.stderr.indexOf('Not a git repository') >= 0) {
clean = true;
}
}

if (!clean) {
/* eslint-disable no-console */
if (force) {
console.log(`WARNING: ${errorMessage}. Forcibly continuing.`);
} else {
console.log('Thank you for using @shopify/polaris-migrator!');
console.log(
chalk.yellow(
'\nBut before we continue, please stash or commit your git changes.',
),
);
console.log(
'\nYou may use the --force flag to override this safety check.',
);
process.exit(1);
}
/* eslint-enable no-console */
}
}

type Args = [
migration: string,
path: string,
flags?: {force?: boolean; dry?: boolean; print?: boolean},
];

export async function run(...args: Args) {
const [migration, files] = args.length ? args : cli.input;
const flags = args && args[2] ? args[2] : cli.flags;

const migrationFile = path.join(
__dirname,
`./migrations/${migration}/${migration}.js`,
);

try {
if (!fs.existsSync(migrationFile)) {
throw new Error(`No migration found for ${migration}`);
}

if (!files) throw new Error(`No path provided for migration`);

if (!flags.dry) {
checkGitStatus(cli.flags.force);
}

const filepaths = globby.sync(files, {cwd: process.cwd()});
if (filepaths.length === 0) {
throw new Error(`No files found for ${files}`);
}

// eslint-disable-next-line no-console
console.log(chalk.green('Running migration:'), migration);

await jscodeshift.run(migrationFile, filepaths, {
dry: flags.dry,
print: flags.print,
babel: true,
ignorePattern: ['**/node_modules/**', '**/.next/**', '**/build/**'],
extensions: 'tsx,ts,jsx,js',
parser: 'tsx',
verbose: 2,
runInBand: true,
silent: false,
stdin: false,
});
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
}
}
1 change: 1 addition & 0 deletions polaris-migrator/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {run} from './cli';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {FileInfo} from 'jscodeshift';
import postcss, {Plugin} from 'postcss';
import valueParser, {Node, WordNode, FunctionNode} from 'postcss-value-parser';

const spacingMap = {
none: '--p-space-0',
'extra-tight': '--p-space-1',
tight: '--p-space-2',
'base-tight': '--p-space-3',
'': '--p-space-4',
base: '--p-space-4',
loose: '--p-space-5',
'extra-loose': '--p-space-8',
};

type Spacing = keyof typeof spacingMap;

function isSpacingFn(node: Node): node is FunctionNode {
return node.type === 'function' && node.value === 'spacing';
}

function isOperator(node: Node): boolean {
return (
node.value === '+' ||
node.value === '-' ||
node.value === '*' ||
node.value === '/'
);
}

const plugin = (): Plugin => ({
postcssPlugin: 'ReplaceSassSpacing',
Declaration(decl) {
const parsed = valueParser(decl.value);

// Skip if the value contains calculations
const containsCalculation = parsed.nodes.some(isOperator);
if (containsCalculation) return;

parsed.walk((node) => {
if (!isSpacingFn(node)) return;
const hasNodes = Boolean(node.nodes && node.nodes.length);
const spacing = hasNodes ? node.nodes[0].value : '';
const newSpacing = spacingMap[spacing as Spacing];

node.value = 'var';
node.nodes = hasNodes ? node.nodes : [{type: 'word'} as WordNode];
node.nodes[0].value = newSpacing;
});

decl.value = parsed.toString();
},
});

export default function replaceSassSpacing(file: FileInfo) {
return postcss(plugin()).process(file.source, {
syntax: require('postcss-scss'),
}).css;
}
Loading