diff --git a/playground/.gitignore b/playground/.gitignore
index 1cc8d80..4447710 100644
--- a/playground/.gitignore
+++ b/playground/.gitignore
@@ -108,12 +108,13 @@ coverage
# Strapi
############################
-# .env
license.txt
exports
-*.cache
+.strapi
+dist
build
.strapi-updater.json
+.strapi-cloud.json
# yalc
.yalc
diff --git a/playground/.strapi/client/app.js b/playground/.strapi/client/app.js
deleted file mode 100644
index 6a5f78e..0000000
--- a/playground/.strapi/client/app.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * This file was automatically generated by Strapi.
- * Any modifications made will be discarded.
- */
-import strapiCloud from "@strapi/plugin-cloud/strapi-admin";
-import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin";
-import configSync from "strapi-plugin-config-sync/strapi-admin";
-import { renderAdmin } from "@strapi/strapi/admin";
-
-renderAdmin(document.getElementById("strapi"), {
- plugins: {
- "strapi-cloud": strapiCloud,
- "users-permissions": usersPermissions,
- "config-sync": configSync,
- },
-});
diff --git a/playground/.strapi/client/index.html b/playground/.strapi/client/index.html
deleted file mode 100644
index 4e9d27c..0000000
--- a/playground/.strapi/client/index.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
- Strapi Admin
-
-
-
-
-
-
-
-
diff --git a/playground/__tests__/cli.test.js b/playground/__tests__/cli.test.js
index e15f4f3..8ace6c6 100644
--- a/playground/__tests__/cli.test.js
+++ b/playground/__tests__/cli.test.js
@@ -58,4 +58,47 @@ describe('Test the config-sync CLI', () => {
}
expect(error).toHaveProperty('code', 1);
});
+
+ test('Import build project', async () => {
+ // First we make sure the dist folder is deleted.
+ await exec('mv dist .tmp');
+
+ await exec('yarn cs import -y');
+
+ const { stdout: buildOutput } = await exec('ls dist');
+ expect(buildOutput).toContain('config');
+ expect(buildOutput).toContain('src');
+ expect(buildOutput).toContain('tsconfig.tsbuildinfo');
+
+ // We restore the dist folder.
+ await exec('rm -rf dist');
+ await exec('mv .tmp/dist ./dist');
+
+ });
+ test('Import project already built', async () => {
+
+
+ // First we make sure the dist folder exists by doing an import.
+ await exec('yarn cs import -y');
+
+ const { stdout: lsDist } = await exec('ls dist');
+ expect(lsDist).toContain('config');
+ expect(lsDist).toContain('src');
+ expect(lsDist).toContain('tsconfig.tsbuildinfo');
+
+ // We remove on file from the dist folder
+ await exec('mv dist/tsconfig.tsbuildinfo .tmp');
+
+ // The new import should not rebuild the project.
+ await exec('yarn cs import -y');
+
+ const { stdout: buildOutput } = await exec('ls dist');
+ expect(buildOutput).toContain('config');
+ expect(buildOutput).toContain('src');
+ expect(buildOutput).not.toContain('tsconfig.tsbuildinfo');
+
+ // We restore the tsconfig.tsbuildinfo file.
+ await exec('mv .tmp/tsconfig.tsbuildinfo dist/tsconfig.tsbuildinfo');
+
+ });
});
diff --git a/playground/__tests__/import-on-boostrap.test.js b/playground/__tests__/import-on-boostrap.test.js
index 3602d92..71352b2 100644
--- a/playground/__tests__/import-on-boostrap.test.js
+++ b/playground/__tests__/import-on-boostrap.test.js
@@ -7,7 +7,7 @@ jest.setTimeout(20000);
afterEach(async () => {
// Disable importOnBootstrap
- await exec('sed -i "s/importOnBootstrap: true/importOnBootstrap: false/g" config/plugins.js');
+ await exec('sed -i "s/importOnBootstrap: true/importOnBootstrap: false/g" config/plugins.ts');
await cleanupStrapi();
await exec('rm -rf config/sync');
@@ -19,8 +19,8 @@ describe('Test the importOnBootstrap feature', () => {
await exec('yarn cs export -y');
await exec('rm -rf .tmp');
- // Manually change the plugins.js to enable importOnBoostrap.
- await exec('sed -i "s/importOnBootstrap: false/importOnBootstrap: true/g" config/plugins.js');
+ // Manually change the plugins.ts to enable importOnBoostrap.
+ await exec('sed -i "s/importOnBootstrap: false/importOnBootstrap: true/g" config/plugins.ts');
// Start up Strapi to initiate the importOnBootstrap function.
await setupStrapi();
@@ -33,8 +33,8 @@ describe('Test the importOnBootstrap feature', () => {
await exec('rm -rf .tmp');
await exec('yarn cs export -y');
- // Manually change the plugins.js to enable importOnBoostrap.
- await exec('sed -i "s/importOnBootstrap: false/importOnBootstrap: true/g" config/plugins.js');
+ // Manually change the plugins.ts to enable importOnBoostrap.
+ await exec('sed -i "s/importOnBootstrap: false/importOnBootstrap: true/g" config/plugins.ts');
// Remove a config file to make sure the importOnBoostrap
// function actually attempts to import.
diff --git a/playground/config/admin.js b/playground/config/admin.ts
similarity index 86%
rename from playground/config/admin.js
rename to playground/config/admin.ts
index 0a18658..c556d27 100644
--- a/playground/config/admin.js
+++ b/playground/config/admin.ts
@@ -1,4 +1,4 @@
-module.exports = ({ env }) => ({
+export default ({ env }) => ({
auth: {
secret: env('ADMIN_JWT_SECRET'),
},
@@ -19,6 +19,5 @@ module.exports = ({ env }) => ({
},
watchIgnoreFiles: [
'!**/.yalc/**/server/**',
- '**/config/sync/**',
- ]
+ ],
});
diff --git a/playground/config/api.js b/playground/config/api.ts
similarity index 80%
rename from playground/config/api.js
rename to playground/config/api.ts
index 62f8b65..37f7c14 100644
--- a/playground/config/api.js
+++ b/playground/config/api.ts
@@ -1,4 +1,4 @@
-module.exports = {
+export default {
rest: {
defaultLimit: 25,
maxLimit: 100,
diff --git a/playground/config/database.js b/playground/config/database.js
deleted file mode 100644
index ac20ae7..0000000
--- a/playground/config/database.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const path = require('path');
-
-module.exports = ({ env }) => ({
- connection: {
- client: 'sqlite',
- connection: {
- filename: path.join(__dirname, '..', env('DATABASE_FILENAME', '.tmp/data.db')),
- },
- useNullAsDefault: true,
- },
-});
diff --git a/playground/config/database.ts b/playground/config/database.ts
new file mode 100644
index 0000000..b424f72
--- /dev/null
+++ b/playground/config/database.ts
@@ -0,0 +1,15 @@
+import path from 'path';
+
+export default ({ env }) => {
+
+ return {
+ connection: {
+ client: 'sqlite',
+ connection: {
+ filename: path.join(__dirname, '..', '..', env('DATABASE_FILENAME', '.tmp/data.db')),
+ },
+ useNullAsDefault: true,
+ },
+ };
+
+};
diff --git a/playground/config/middlewares.js b/playground/config/middlewares.ts
similarity index 91%
rename from playground/config/middlewares.js
rename to playground/config/middlewares.ts
index 6eaf586..829f5c0 100644
--- a/playground/config/middlewares.js
+++ b/playground/config/middlewares.ts
@@ -1,4 +1,4 @@
-module.exports = [
+export default [
'strapi::logger',
'strapi::errors',
'strapi::security',
diff --git a/playground/config/plugins.js b/playground/config/plugins.ts
similarity index 80%
rename from playground/config/plugins.js
rename to playground/config/plugins.ts
index e321c68..4434266 100644
--- a/playground/config/plugins.js
+++ b/playground/config/plugins.ts
@@ -1,4 +1,4 @@
-module.exports = {
+export default () => ({
'config-sync': {
enabled: true,
config: {
@@ -6,4 +6,4 @@ module.exports = {
minify: true,
},
},
-};
+});
diff --git a/playground/config/server.js b/playground/config/server.ts
similarity index 86%
rename from playground/config/server.js
rename to playground/config/server.ts
index 039daec..a54a241 100644
--- a/playground/config/server.js
+++ b/playground/config/server.ts
@@ -1,4 +1,4 @@
-module.exports = ({ env }) => ({
+export default ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
app: {
diff --git a/playground/jsconfig.json b/playground/jsconfig.json
deleted file mode 100644
index 4ebd927..0000000
--- a/playground/jsconfig.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "compilerOptions": {
- "moduleResolution": "nodenext",
- "target": "ES2021",
- "checkJs": true,
- "allowJs": true
- }
-}
diff --git a/playground/package.json b/playground/package.json
index 1f0de37..eef7ebe 100644
--- a/playground/package.json
+++ b/playground/package.json
@@ -11,15 +11,19 @@
"cs": "config-sync"
},
"devDependencies": {
+ "@types/node": "^24.0.7",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
"supertest": "^6.3.3",
+ "typescript": "^5.8.3",
"yalc": "^1.0.0-pre.53"
},
"dependencies": {
- "@strapi/plugin-cloud": "5.0.4",
- "@strapi/plugin-users-permissions": "5.0.4",
- "@strapi/strapi": "5.0.4",
+ "@strapi/plugin-cloud": "5.16.0",
+ "@strapi/plugin-users-permissions": "5.16.0",
+ "@strapi/strapi": "5.16.0",
"better-sqlite3": "9.4.3",
"react": "^18.0.0",
"react-dom": "^18.0.0",
diff --git a/playground/src/admin/app.example.js b/playground/src/admin/app.example.tsx
similarity index 84%
rename from playground/src/admin/app.example.js
rename to playground/src/admin/app.example.tsx
index 7316221..3ea4d91 100644
--- a/playground/src/admin/app.example.js
+++ b/playground/src/admin/app.example.tsx
@@ -1,3 +1,5 @@
+import type { StrapiApp } from '@strapi/strapi/admin';
+
export default {
config: {
locales: [
@@ -29,7 +31,7 @@ export default {
// 'zh',
],
},
- bootstrap(app) {
+ bootstrap(app: StrapiApp) {
console.log(app);
},
};
diff --git a/playground/src/admin/tsconfig.json b/playground/src/admin/tsconfig.json
new file mode 100644
index 0000000..083046e
--- /dev/null
+++ b/playground/src/admin/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["../plugins/**/admin/src/**/*", "./"],
+ "exclude": ["node_modules/", "build/", "dist/", "**/*.test.ts"]
+}
diff --git a/playground/src/admin/webpack.config.example.js b/playground/src/admin/webpack.config.example.js
deleted file mode 100644
index 1ca45c2..0000000
--- a/playground/src/admin/webpack.config.example.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/* eslint-disable no-unused-vars */
-module.exports = (config, webpack) => {
- // Note: we provide webpack above so you should not `require` it
- // Perform customizations to webpack config
- // Important: return the modified config
- return config;
-};
diff --git a/playground/src/admin/webpack.config.example.ts b/playground/src/admin/webpack.config.example.ts
new file mode 100644
index 0000000..85f6982
--- /dev/null
+++ b/playground/src/admin/webpack.config.example.ts
@@ -0,0 +1,12 @@
+import { mergeConfig, type UserConfig } from 'vite';
+
+export default (config: UserConfig) => {
+ // Important: always return the modified config
+ return mergeConfig(config, {
+ resolve: {
+ alias: {
+ '@': '/src',
+ },
+ },
+ });
+};
diff --git a/playground/src/api/home/controllers/home.js b/playground/src/api/home/controllers/home.js
deleted file mode 100644
index b4dc6c0..0000000
--- a/playground/src/api/home/controllers/home.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/**
- * home controller
- */
-
-const { createCoreController } = require('@strapi/strapi').factories;
-
-module.exports = createCoreController('api::home.home');
diff --git a/playground/src/api/home/controllers/home.ts b/playground/src/api/home/controllers/home.ts
new file mode 100644
index 0000000..d468c8e
--- /dev/null
+++ b/playground/src/api/home/controllers/home.ts
@@ -0,0 +1,7 @@
+/**
+ * home controller
+ */
+
+import { factories } from '@strapi/strapi';
+
+export default factories.createCoreController('api::home.home');
diff --git a/playground/src/api/home/routes/home.js b/playground/src/api/home/routes/home.js
deleted file mode 100644
index 63ca7fd..0000000
--- a/playground/src/api/home/routes/home.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/**
- * home router
- */
-
-const { createCoreRouter } = require('@strapi/strapi').factories;
-
-module.exports = createCoreRouter('api::home.home');
diff --git a/playground/src/api/home/routes/home.ts b/playground/src/api/home/routes/home.ts
new file mode 100644
index 0000000..b1cd7b0
--- /dev/null
+++ b/playground/src/api/home/routes/home.ts
@@ -0,0 +1,8 @@
+
+/**
+ * home router
+ */
+
+import { factories } from '@strapi/strapi';
+
+export default factories.createCoreRouter('api::home.home');
diff --git a/playground/src/api/home/services/home.js b/playground/src/api/home/services/home.js
deleted file mode 100644
index 64e6d32..0000000
--- a/playground/src/api/home/services/home.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/**
- * home service
- */
-
-const { createCoreService } = require('@strapi/strapi').factories;
-
-module.exports = createCoreService('api::home.home');
diff --git a/playground/src/api/home/services/home.ts b/playground/src/api/home/services/home.ts
new file mode 100644
index 0000000..441af19
--- /dev/null
+++ b/playground/src/api/home/services/home.ts
@@ -0,0 +1,7 @@
+/**
+ * home service
+ */
+
+import { factories } from '@strapi/strapi';
+
+export default factories.createCoreService('api::home.home');
diff --git a/playground/src/api/page/controllers/page.js b/playground/src/api/page/controllers/page.js
deleted file mode 100644
index 61fe8a7..0000000
--- a/playground/src/api/page/controllers/page.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/**
- * page controller
- */
-
-const { createCoreController } = require('@strapi/strapi').factories;
-
-module.exports = createCoreController('api::page.page');
diff --git a/playground/src/api/page/controllers/page.ts b/playground/src/api/page/controllers/page.ts
new file mode 100644
index 0000000..041e9d6
--- /dev/null
+++ b/playground/src/api/page/controllers/page.ts
@@ -0,0 +1,7 @@
+/**
+ * page controller
+ */
+
+import { factories } from '@strapi/strapi';
+
+export default factories.createCoreController('api::page.page');
diff --git a/playground/src/api/page/routes/page.js b/playground/src/api/page/routes/page.js
deleted file mode 100644
index 258307a..0000000
--- a/playground/src/api/page/routes/page.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/**
- * page router
- */
-
-const { createCoreRouter } = require('@strapi/strapi').factories;
-
-module.exports = createCoreRouter('api::page.page');
diff --git a/playground/src/api/page/routes/page.ts b/playground/src/api/page/routes/page.ts
new file mode 100644
index 0000000..8e5ddfc
--- /dev/null
+++ b/playground/src/api/page/routes/page.ts
@@ -0,0 +1,7 @@
+/**
+ * page router
+ */
+
+import { factories } from '@strapi/strapi';
+
+export default factories.createCoreRouter('api::page.page');
diff --git a/playground/src/api/page/services/page.js b/playground/src/api/page/services/page.js
deleted file mode 100644
index d5abde4..0000000
--- a/playground/src/api/page/services/page.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-/**
- * page service
- */
-
-const { createCoreService } = require('@strapi/strapi').factories;
-
-module.exports = createCoreService('api::page.page');
diff --git a/playground/src/api/page/services/page.ts b/playground/src/api/page/services/page.ts
new file mode 100644
index 0000000..df7f80a
--- /dev/null
+++ b/playground/src/api/page/services/page.ts
@@ -0,0 +1,7 @@
+/**
+ * page service
+ */
+
+import { factories } from '@strapi/strapi';
+
+module.exports = factories.createCoreService('api::page.page');
diff --git a/playground/src/index.js b/playground/src/index.ts
similarity index 68%
rename from playground/src/index.js
rename to playground/src/index.ts
index ac5feae..58be268 100644
--- a/playground/src/index.js
+++ b/playground/src/index.ts
@@ -1,13 +1,13 @@
-'use strict';
+// import type { Core } from '@strapi/strapi';
-module.exports = {
+export default {
/**
* An asynchronous register function that runs before
* your application is initialized.
*
* This gives you an opportunity to extend code.
*/
- register(/*{ strapi }*/) {},
+ register(/* { strapi }: { strapi: Core.Strapi } */) {},
/**
* An asynchronous bootstrap function that runs before
@@ -16,5 +16,5 @@ module.exports = {
* This gives you an opportunity to set up your data model,
* run jobs, or perform some special logic.
*/
- bootstrap(/*{ strapi }*/) {},
+ bootstrap(/* { strapi }: { strapi: Core.Strapi } */) {},
};
diff --git a/playground/tsconfig.json b/playground/tsconfig.json
new file mode 100644
index 0000000..bd6b9ad
--- /dev/null
+++ b/playground/tsconfig.json
@@ -0,0 +1,43 @@
+{
+ "compilerOptions": {
+ "module": "CommonJS",
+ "moduleResolution": "Node",
+ "lib": ["ES2020"],
+ "target": "ES2019",
+ "strict": false,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "incremental": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "noEmitOnError": true,
+ "noImplicitThis": true,
+ "outDir": "dist",
+ "rootDir": ".",
+ },
+ "include": [
+ // Include root files
+ "./",
+ // Include all ts files
+ "./**/*.ts",
+ // Include all js files
+ "./**/*.js",
+ // Force the JSON files in the src folder to be included
+ "src/**/*.json"
+ ],
+
+ "exclude": [
+ "node_modules/",
+ "build/",
+ "dist/",
+ ".cache/",
+ ".tmp/",
+
+ // Do not include admin files in the server compilation
+ "src/admin/",
+ // Do not include test files
+ "**/*.test.*",
+ // Do not include plugins in the server compilation
+ "src/plugins/**"
+ ]
+}
diff --git a/server/cli.js b/server/cli.js
index 18f8102..d062630 100644
--- a/server/cli.js
+++ b/server/cli.js
@@ -8,6 +8,7 @@ import inquirer from 'inquirer';
import isEmpty from 'lodash/isEmpty';
import { createStrapi, compileStrapi } from '@strapi/strapi';
import gitDiff from 'git-diff';
+import tsUtils from '@strapi/typescript-utils';
import warnings from './warnings';
import packageJSON from '../package.json';
@@ -17,7 +18,19 @@ const program = new Command();
const getStrapiApp = async () => {
process.env.CONFIG_SYNC_CLI = 'true';
- const appContext = await compileStrapi();
+ const appDir = process.cwd();
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
+ const outDir = await tsUtils.resolveOutDir(appDir);
+ const alreadyCompiled = await fs.existsSync(outDir);
+
+ let appContext;
+ if (!isTSProject || !alreadyCompiled) {
+ appContext = await compileStrapi();
+ } else {
+ const distDir = isTSProject ? outDir : appDir;
+ appContext = { appDir, distDir };
+ }
+
const app = await createStrapi(appContext).load();
return app;
};