mirror of
https://github.com/mastodon/mastodon.git
synced 2026-01-17 07:16:39 +00:00
basic changes to get a build working
This commit is contained in:
parent
aee820f49e
commit
fa4243c28c
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -74,3 +74,5 @@ docker-compose.override.yml
|
|||
|
||||
# Ignore local-only rspec configuration
|
||||
.rspec-local
|
||||
|
||||
/.dist
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import './public-path';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import Rails from '@rails/ujs';
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import './public-path';
|
||||
import { start } from 'mastodon/common';
|
||||
import { loadLocale } from 'mastodon/locales';
|
||||
import main from 'mastodon/main';
|
||||
|
||||
import { start } from '../mastodon/common';
|
||||
import { loadLocale } from '../mastodon/locales';
|
||||
import { loadPolyfills } from '../mastodon/polyfills';
|
||||
import { loadPolyfills } from 'mastodon/polyfills';
|
||||
|
||||
start();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import './public-path';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { afterInitialRender } from 'mastodon/hooks/useRenderSignal';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import './public-path';
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
ready(() => {
|
||||
|
|
|
|||
8
app/javascript/entrypoints/index.html
Normal file
8
app/javascript/entrypoints/index.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="module" src="./application.ts"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mastodon"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +1 @@
|
|||
import '../styles/mailer.scss';
|
||||
|
||||
require.context('../icons');
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
// Dynamically set webpack's loading path depending on a meta header, in order
|
||||
// to share the same assets regardless of instance configuration.
|
||||
// See https://webpack.js.org/guides/public-path/#on-the-fly
|
||||
|
||||
function removeOuterSlashes(string: string) {
|
||||
return string.replace(/^\/*/, '').replace(/\/*$/, '');
|
||||
}
|
||||
|
||||
function formatPublicPath(host = '', path = '') {
|
||||
let formattedHost = removeOuterSlashes(host);
|
||||
if (formattedHost && !/^http/i.test(formattedHost)) {
|
||||
formattedHost = `//${formattedHost}`;
|
||||
}
|
||||
const formattedPath = removeOuterSlashes(path);
|
||||
return `${formattedHost}/${formattedPath}/`;
|
||||
}
|
||||
|
||||
const cdnHost = document.querySelector<HTMLMetaElement>('meta[name=cdn-host]');
|
||||
|
||||
__webpack_public_path__ = formatPublicPath(
|
||||
cdnHost ? cdnHost.content : '',
|
||||
process.env.PUBLIC_OUTPUT_PATH,
|
||||
);
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import './public-path';
|
||||
|
||||
import { IntlMessageFormat } from 'intl-messageformat';
|
||||
import type { MessageDescriptor, PrimitiveType } from 'react-intl';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ and performs no other task.
|
|||
|
||||
*/
|
||||
|
||||
import './public-path';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
interface JRDLink {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import './public-path';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { start } from '../mastodon/common';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import './public-path';
|
||||
import axios from 'axios';
|
||||
|
||||
import ready from '../mastodon/ready';
|
||||
|
|
|
|||
|
|
@ -10,18 +10,17 @@
|
|||
// version: 3
|
||||
|
||||
// This json file contains the names of the categories.
|
||||
const emojiMart5LocalesData = require('@emoji-mart/data/i18n/en.json');
|
||||
const emojiMart5Data = require('@emoji-mart/data/sets/15/all.json');
|
||||
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
|
||||
const _ = require('lodash');
|
||||
import emojiMart5LocalesData from '@emoji-mart/data/i18n/en.json';
|
||||
import emojiMart5Data from '@emoji-mart/data/sets/15/all.json';
|
||||
import { uncompress as emojiMartUncompress } from 'emoji-mart/dist/utils/data';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
const emojiMap = require('./emoji_map.json');
|
||||
import emojiMap from './emoji_map.json';
|
||||
// This json file is downloaded from https://github.com/iamcal/emoji-data/
|
||||
// and is used to correct the sheet coordinates since we're using that repo's sheet
|
||||
const emojiSheetData = require('./emoji_sheet.json');
|
||||
const { unicodeToFilename } = require('./unicode_to_filename');
|
||||
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
|
||||
import emojiSheetData from './emoji_sheet.json';
|
||||
import { unicodeToFilename } from './unicode_to_filename';
|
||||
import { unicodeToUnifiedName } from './unicode_to_unified_name';
|
||||
|
||||
// Grabbed from `emoji_utils` to avoid circular dependency
|
||||
function unifiedToNative(unified) {
|
||||
|
|
@ -181,7 +180,7 @@ Object.keys(emojiMartData.emojis).forEach(key => {
|
|||
|
||||
// JSON.parse/stringify is to emulate what @preval is doing and avoid any
|
||||
// inconsistent behavior in dev mode
|
||||
module.exports = JSON.parse(JSON.stringify([
|
||||
export default JSON.parse(JSON.stringify([
|
||||
shortCodesToEmojiData,
|
||||
/*
|
||||
* The property `skins` is not found in the current context.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// taken from:
|
||||
// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866
|
||||
exports.unicodeToFilename = (str) => {
|
||||
export const unicodeToFilename = (str) => {
|
||||
let result = '';
|
||||
let charCode = 0;
|
||||
let p = 0;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ function padLeft(str, num) {
|
|||
return str;
|
||||
}
|
||||
|
||||
exports.unicodeToUnifiedName = (str) => {
|
||||
export const unicodeToUnifiedName = (str) => {
|
||||
let output = '';
|
||||
|
||||
for (let i = 0; i < str.length; i += 2) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import { isLocaleLoaded, setLocale } from './global_locale';
|
|||
|
||||
const localeLoadingSemaphore = new Semaphore(1);
|
||||
|
||||
const localeFiles = import.meta.glob<{ default: LocaleData['messages'] }>([
|
||||
'./*.json',
|
||||
]);
|
||||
|
||||
export async function loadLocale() {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings
|
||||
const locale = document.querySelector<HTMLElement>('html')?.lang || 'en';
|
||||
|
|
@ -17,13 +21,14 @@ export async function loadLocale() {
|
|||
// if the locale is already set, then do nothing
|
||||
if (isLocaleLoaded()) return;
|
||||
|
||||
const localeData = (await import(
|
||||
/* webpackMode: "lazy" */
|
||||
/* webpackChunkName: "locale/[request]" */
|
||||
/* webpackInclude: /\.json$/ */
|
||||
/* webpackPreload: true */
|
||||
`mastodon/locales/${locale}.json`
|
||||
)) as LocaleData['messages'];
|
||||
// If there is no locale file, then fallback to english
|
||||
const localeFile = Object.hasOwn(localeFiles, '`./${locale}.json`')
|
||||
? localeFiles[`./${locale}.json`]
|
||||
: localeFiles[`./en.json`];
|
||||
|
||||
if (!localeFile) throw new Error('Could not load the locale JSON file');
|
||||
|
||||
const { default: localeData } = await localeFile();
|
||||
|
||||
setLocale({ messages: localeData, locale });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,11 +54,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
|
|||
return;
|
||||
}
|
||||
// Load the polyfill 1st BEFORE loading data
|
||||
await import('@formatjs/intl-pluralrules/polyfill-force');
|
||||
await import(
|
||||
/* webpackChunkName: "i18n-pluralrules-polyfill" */ '@formatjs/intl-pluralrules/polyfill-force'
|
||||
);
|
||||
await import(
|
||||
/* webpackChunkName: "i18n-pluralrules-polyfill-[request]" */ `@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}`
|
||||
`../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
90
config/vite/plugin-manifest-sri.ts
Normal file
90
config/vite/plugin-manifest-sri.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { createHash } from 'node:crypto';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import type { Plugin, Manifest } from 'vite';
|
||||
|
||||
export type Algorithm = 'sha256' | 'sha384' | 'sha512';
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
* Which hashing algorithms to use when calculate the integrity hash for each
|
||||
* asset in the manifest.
|
||||
* @default ['sha384']
|
||||
*/
|
||||
algorithms?: Algorithm[];
|
||||
|
||||
/**
|
||||
* Path of the manifest files that should be read and augmented with the
|
||||
* integrity hash, relative to `outDir`.
|
||||
* @default ['manifest.json', 'manifest-assets.json']
|
||||
*/
|
||||
manifestPaths?: string[];
|
||||
}
|
||||
|
||||
declare module 'vite' {
|
||||
interface ManifestChunk {
|
||||
integrity: string;
|
||||
}
|
||||
}
|
||||
|
||||
export function manifestSRI(options: Options = {}): Plugin {
|
||||
const {
|
||||
algorithms = ['sha384'],
|
||||
manifestPaths = [
|
||||
'.vite/manifest.json',
|
||||
'.vite/manifest-assets.json',
|
||||
'manifest.json',
|
||||
'manifest-assets.json',
|
||||
],
|
||||
} = options;
|
||||
|
||||
return {
|
||||
name: 'vite-plugin-manifest-sri',
|
||||
apply: 'build',
|
||||
enforce: 'post',
|
||||
async writeBundle({ dir }) {
|
||||
await Promise.all(
|
||||
manifestPaths.map((path) =>
|
||||
augmentManifest(path, algorithms, dir ?? ''),
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function augmentManifest(
|
||||
manifestPath: string,
|
||||
algorithms: string[],
|
||||
outDir: string,
|
||||
) {
|
||||
const resolveInOutDir = (path: string) => resolve(outDir, path);
|
||||
manifestPath = resolveInOutDir(manifestPath);
|
||||
|
||||
const manifest: Manifest | undefined = await fs
|
||||
.readFile(manifestPath, 'utf-8')
|
||||
.then((file) => JSON.parse(file) as Manifest);
|
||||
|
||||
if (manifest) {
|
||||
await Promise.all(
|
||||
Object.values(manifest).map(async (chunk) => {
|
||||
chunk.integrity = integrityForAsset(
|
||||
await fs.readFile(resolveInOutDir(chunk.file)),
|
||||
algorithms,
|
||||
);
|
||||
}),
|
||||
);
|
||||
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
function integrityForAsset(source: Buffer, algorithms: string[]) {
|
||||
return algorithms
|
||||
.map((algorithm) => calculateIntegrityHash(source, algorithm))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export function calculateIntegrityHash(source: Buffer, algorithm: string) {
|
||||
const hash = createHash(algorithm).update(source).digest().toString('base64');
|
||||
return `${algorithm.toLowerCase()}-${hash}`;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"target": "esnext",
|
||||
"module": "CommonJS",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["vitest/globals", "@types/webpack-env"],
|
||||
"types": ["vite/client", "vitest/globals", "@types/webpack-env"],
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "tmp/cache/tsconfig.tsbuildinfo",
|
||||
|
|
|
|||
54
vite.config.ts
Normal file
54
vite.config.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import path from 'node:path';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
import { manifestSRI } from './config/vite/plugin-manifest-sri';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineConfig({
|
||||
root: './app/javascript/entrypoints',
|
||||
build: {
|
||||
commonjsOptions: { transformMixedEsModules: true },
|
||||
outDir: path.resolve(__dirname, '.dist'),
|
||||
emptyOutDir: true,
|
||||
manifest: 'manifest.json',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
chunkFileNames: (chunkInfo) => {
|
||||
if (
|
||||
chunkInfo.facadeModuleId?.match(
|
||||
/mastodon\/locales\/[a-zA-Z-]+\.json/,
|
||||
)
|
||||
) {
|
||||
// put all locale files in `intl/`
|
||||
return `intl/[name]-[hash].js`;
|
||||
} else if (
|
||||
chunkInfo.facadeModuleId?.match(/node_modules\/@formatjs\//)
|
||||
) {
|
||||
// use a custom name for formatjs polyfill files
|
||||
const name = /node_modules\/@formatjs\/([^/]+)\//.exec(
|
||||
chunkInfo.facadeModuleId,
|
||||
);
|
||||
|
||||
if (name?.[1]) return `intl/[name]-${name[1]}-[hash].js`;
|
||||
} else if (chunkInfo.name === 'index' && chunkInfo.facadeModuleId) {
|
||||
// Use a custom name for chunks, to avoid having too many of them called "index"
|
||||
const parts = chunkInfo.facadeModuleId.split('/');
|
||||
|
||||
const parent = parts.at(-2);
|
||||
|
||||
if (parent) return `${parent}-[name]-[hash].js`;
|
||||
}
|
||||
return `[name]-[hash].js`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [manifestSRI()],
|
||||
resolve: {
|
||||
alias: {
|
||||
mastodon: path.resolve(__dirname, 'app/javascript/mastodon'),
|
||||
'@': path.resolve(__dirname, 'app/javascript'),
|
||||
},
|
||||
},
|
||||
});
|
||||
32
yarn.lock
32
yarn.lock
|
|
@ -2533,14 +2533,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint-community/regexpp@npm:^4.10.0":
|
||||
version: 4.10.0
|
||||
resolution: "@eslint-community/regexpp@npm:4.10.0"
|
||||
checksum: 10c0/c5f60ef1f1ea7649fa7af0e80a5a79f64b55a8a8fa5086de4727eb4c86c652aedee407a9c143b8995d2c0b2d75c1222bec9ba5d73dbfc1f314550554f0979ef4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint-community/regexpp@npm:^4.12.1":
|
||||
"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1":
|
||||
version: 4.12.1
|
||||
resolution: "@eslint-community/regexpp@npm:4.12.1"
|
||||
checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6
|
||||
|
|
@ -4118,16 +4111,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/babel__traverse@npm:*":
|
||||
version: 7.20.3
|
||||
resolution: "@types/babel__traverse@npm:7.20.3"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.20.7"
|
||||
checksum: 10c0/295ed9b837e62e17ee43be0df45d90fff5208986bd43af593c9020d152d3b2c55328e038c2f8585926b63cc22f887f28bf3f4c805aa881e2dd0bdd5ead92ece0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/babel__traverse@npm:^7.20.6":
|
||||
"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.20.6":
|
||||
version: 7.20.6
|
||||
resolution: "@types/babel__traverse@npm:7.20.6"
|
||||
dependencies:
|
||||
|
|
@ -12068,17 +12052,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromatch@npm:^4.0.4":
|
||||
version: 4.0.7
|
||||
resolution: "micromatch@npm:4.0.7"
|
||||
dependencies:
|
||||
braces: "npm:^3.0.3"
|
||||
picomatch: "npm:^2.3.1"
|
||||
checksum: 10c0/58fa99bc5265edec206e9163a1d2cec5fabc46a5b473c45f4a700adce88c2520456ae35f2b301e4410fb3afb27e9521fb2813f6fc96be0a48a89430e0916a772
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromatch@npm:^4.0.5, micromatch@npm:^4.0.8, micromatch@npm:~4.0.8":
|
||||
"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8, micromatch@npm:~4.0.8":
|
||||
version: 4.0.8
|
||||
resolution: "micromatch@npm:4.0.8"
|
||||
dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user