Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ sample-apps/*/public/build/*
!sample-apps/**/shopify.extension.toml
!sample-apps/**/locales/*.json
dev.sqlite
shopify.app.toml
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@ yarn expand-liquid vanilla-js
yarn expand-liquid typescript
```

### Update API Versions and Function Schemas

To update API versions and function schemas automatically:

```shell
# Step 1: Link to a Shopify app to create shopify.app.toml with client_id
shopify app config link

# Step 2: Generate/update the extension directories list in shopify.app.toml
yarn generate-app

# Step 3: Run the comprehensive update command
yarn update-api-version
```

This process:
1. First, links to a Shopify app to create shopify.app.toml with the client ID
2. Then adds all extension directories to the same file (preserving the client_id)
3. Finally, runs a sequence of commands that:
- Updates API versions across all extensions
- Expands liquid templates
- Updates function schemas

### Run Tests

```shell
Expand All @@ -34,3 +57,19 @@ cargo test
cargo fmt
cargo clippy -- -D warnings
```

### Update Dependencies

To check and update JavaScript dependencies in all package.json.liquid files:

```shell
yarn check-js-dependencies
```

To check and update Rust dependencies in all Cargo.toml and Cargo.toml.liquid files:

```shell
yarn check-rust-dependencies
```

These utilities will fetch the latest versions from npm and crates.io respectively and update your templates.
21 changes: 15 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,31 @@
"version": "1.0.0",
"type": "module",
"devDependencies": {
"@iarna/toml": "^2.2.5",
"fast-glob": "^3.2.11",
"liquidjs": "^9.37.0",
"@graphql-codegen/cli": "^3.2.2",
"@graphql-codegen/typescript": "^3.0.2",
"@graphql-codegen/typescript-operations": "^3.0.2",
"graphql": "^16.6.0"
"@iarna/toml": "^2.2.5",
"@shopify/toml-patch": "^1.0.0",
"dayjs": "^1.11.11",
"fast-glob": "^3.2.11",
"graphql": "^16.6.0",
"liquidjs": "^9.37.0"
},
"scripts": {
"expand-liquid": "node ./util/expand-liquid.js",
"typegen": "yarn workspaces run graphql-code-generator --config package.json",
"test-js": "yarn expand-liquid vanilla-js && yarn && yarn typegen && yarn workspaces run test run",
"test-ts": "yarn expand-liquid typescript && yarn && yarn typegen && yarn workspaces run test run"
"test-ts": "yarn expand-liquid typescript && yarn && yarn typegen && yarn workspaces run test run",
"check-api-version": "node ./util/check-api-version.js",
"check-js-dependencies": "node ./util/check-js-dependencies.js",
"check-rust-dependencies": "node ./util/check-rust-dependencies.js",
"check-rust": "yarn check-rust-dependencies && yarn expand-liquid && cargo test",
"generate-app": "node ./util/generate-app.js",
"update-schemas": "node ./util/update-schemas.js",
"update-api-version": "yarn check-api-version && yarn expand-liquid && yarn update-schemas"
},
"private": true,
"workspaces": [
"*/javascript/**"
]
}
}
114 changes: 114 additions & 0 deletions util/check-api-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import fs from 'fs/promises';
import fastGlob from 'fast-glob';
import dayjs from 'dayjs';
import { updateTomlValues } from '@shopify/toml-patch';

const ROOT_DIR = '.';
const FILE_PATTERN = '**/shopify.extension.toml.liquid';
const LIQUID_PLACEHOLDER = 'LIQUID_PLACEHOLDER';

// Method to get the latest API version based on today's date
function getLatestApiVersion() {
const date = dayjs();
const year = date.year();
const month = date.month();
const quarter = Math.floor(month / 3);

const monthNum = quarter * 3 + 1;
const paddedMonth = String(monthNum).padStart(2, '0');

return `${year}-${paddedMonth}`;
}

// Method to find all shopify.extension.toml.liquid files
async function findAllExtensionFiles() {
return fastGlob(FILE_PATTERN, { cwd: ROOT_DIR, absolute: true });
}

// Function to preprocess liquid syntax
function preprocessLiquidSyntax(content) {
const liquidExpressions = [];
const placeholderContent = content.replace(/\{\{.*?\}\}|\{%\s.*?\s%\}/g, (match) => {
liquidExpressions.push(match);
return `{${LIQUID_PLACEHOLDER}:${liquidExpressions.length - 1}}`;
});
return { placeholderContent, liquidExpressions };
}

// Function to restore liquid syntax
function restoreLiquidSyntax(content, liquidExpressions) {
return content.replace(new RegExp(`\\{${LIQUID_PLACEHOLDER}:(\\d+)\\}`, 'g'), (match, index) => {
return liquidExpressions[Number(index)];
});
}

// Method to update the API version in the file using toml-patch
async function updateApiVersion(filePath, latestVersion) {
try {
const content = await fs.readFile(filePath, 'utf8');

// Handle liquid templates if needed
const isLiquidFile = filePath.endsWith('.liquid');
let liquidExpressions = [];
let processedContent = content;

if (isLiquidFile) {
const processed = preprocessLiquidSyntax(content);
processedContent = processed.placeholderContent;
liquidExpressions = processed.liquidExpressions;
}

// Use toml-patch to update the API version
const updates = [
[['api_version'], latestVersion]
];

let updatedContent = updateTomlValues(processedContent, updates);

// Restore liquid syntax if needed
if (isLiquidFile) {
updatedContent = restoreLiquidSyntax(updatedContent, liquidExpressions);
}

await fs.writeFile(filePath, updatedContent, 'utf8');
console.log(`Updated API version in ${filePath} to ${latestVersion}`);

} catch (error) {
console.error(`Error updating API version in ${filePath}:`, error.message);
}
}

// Main method to check and update API versions
async function checkAndUpdateApiVersions() {
const latestVersion = getLatestApiVersion();
console.log(`Latest API version: ${latestVersion}`);
const extensionFiles = await findAllExtensionFiles();
console.log(`Found ${extensionFiles.length} extension files to check`);

for (const filePath of extensionFiles) {
try {
const content = await fs.readFile(filePath, 'utf8');
const match = content.match(/api_version\s*=\s*"(\d{4}-\d{2})"/);

if (match) {
const currentVersion = match[1];

if (currentVersion !== latestVersion) {
console.log(`Updating ${filePath} from ${currentVersion} to ${latestVersion}`);
await updateApiVersion(filePath, latestVersion);
} else {
console.log(`API version in ${filePath} is already up to date (${currentVersion}).`);
}
} else {
console.warn(`No API version found in ${filePath}`);
}
} catch (error) {
console.error(`Error processing ${filePath}:`, error.message);
}
}
}

checkAndUpdateApiVersions().catch(error => {
console.error('Error checking and updating API versions:', error);
process.exit(1);
});
58 changes: 58 additions & 0 deletions util/check-js-dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import fs from 'fs/promises';
import path from 'path';
import fastGlob from 'fast-glob';
import { execSync } from 'child_process';

const ROOT_DIR = '.';
const FILE_PATTERN = '**/package.json.liquid';

async function findAllPackageFiles() {
return fastGlob(FILE_PATTERN, { cwd: ROOT_DIR, absolute: true });
}

async function getLatestVersion(packageName) {
try {
// Fetch the latest version of a package from the npm registry
const output = execSync(`npm show ${packageName} version`, { encoding: 'utf8' });
return output.trim();
} catch (error) {
console.warn(`Could not fetch version for package ${packageName}:`, error.message);
return null;
}
}

async function checkAndUpdateDependencies(filePath) {
const content = await fs.readFile(filePath, 'utf8');
const jsonContent = JSON.parse(content);

const { dependencies = {}, devDependencies = {} } = jsonContent;

const updateDependencyVersion = async (dependencies) => {
for (const [name, currentVersion] of Object.entries(dependencies)) {
const latestVersion = await getLatestVersion(name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we ensure that it's safe to update to the latest version? For example, if there's a major version bump. Are we relying on tests to tell us that something is wrong?

if (latestVersion && currentVersion !== `^${latestVersion}`) {
console.log(`Updating ${name} from ${currentVersion} to ^${latestVersion}`);
dependencies[name] = `^${latestVersion}`;
}
}
};

await updateDependencyVersion(dependencies);
await updateDependencyVersion(devDependencies);

const updatedContent = JSON.stringify(jsonContent, null, 2);
await fs.writeFile(filePath, updatedContent, 'utf8');
console.log(`Updated dependencies in ${filePath}`);
}

async function main() {
const packageFiles = await findAllPackageFiles();
for (const filePath of packageFiles) {
await checkAndUpdateDependencies(filePath);
}
}

main().catch(error => {
console.error('Error checking and updating dependencies:', error);
process.exit(1);
});
Loading
Loading