feat(client): ts-migrate client/utils/** (#42823)
* rename js files * update imports and references * migrate build-challenges * migrate challenge-types * migrate utils/index * migrate state-management * install @types/psl for tags * migrate tags * migrate tags.test * migrate challenge-page-creator * migrate utils/gatsby/index * migrate layout-selector * migrate layout-selector.test * revert challenge-types Curriculum can't handle TS or modules * convert arrow functions * revert build-challenges * revert utils/gatsby/index * revert challenge-page-creator * revert challenge-types reference * Delete state-management Deleted in #42960 * Disable render-result-naming-convention (for now) * update layout-selector.test comment * reorder imports in build-challenges * change ts-ignore to ts-expect-error
This commit is contained in:
@ -4,7 +4,7 @@ const {
|
|||||||
buildChallenges,
|
buildChallenges,
|
||||||
replaceChallengeNode,
|
replaceChallengeNode,
|
||||||
localeChallengesRootDir
|
localeChallengesRootDir
|
||||||
} = require('./utils/buildChallenges');
|
} = require('./utils/build-challenges');
|
||||||
|
|
||||||
const { clientLocale, curriculumLocale, homeLocation } = envData;
|
const { clientLocale, curriculumLocale, homeLocation } = envData;
|
||||||
|
|
||||||
|
6
client/package-lock.json
generated
6
client/package-lock.json
generated
@ -4461,6 +4461,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
||||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||||
},
|
},
|
||||||
|
"@types/psl": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/psl/-/psl-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-HhZnoLAvI2koev3czVPzBNRYvdrzJGLjQbWZhqFmS9Q6a0yumc5qtfSahBGb5g+6qWvA8iiQktqGkwoIXa/BNQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/reach__router": {
|
"@types/reach__router": {
|
||||||
"version": "1.3.9",
|
"version": "1.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.9.tgz",
|
||||||
|
@ -138,6 +138,7 @@
|
|||||||
"@types/loadable__component": "5.13.4",
|
"@types/loadable__component": "5.13.4",
|
||||||
"@types/lodash-es": "4.17.4",
|
"@types/lodash-es": "4.17.4",
|
||||||
"@types/prismjs": "1.16.6",
|
"@types/prismjs": "1.16.6",
|
||||||
|
"@types/psl": "^1.1.0",
|
||||||
"@types/reach__router": "1.3.9",
|
"@types/reach__router": "1.3.9",
|
||||||
"@types/react-dom": "17.0.9",
|
"@types/react-dom": "17.0.9",
|
||||||
"@types/react-helmet": "6.1.2",
|
"@types/react-helmet": "6.1.2",
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
import store from 'store';
|
import store from 'store';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import { backEndProject } from '../../utils/challengeTypes';
|
import { backEndProject } from '../../utils/challenge-types';
|
||||||
import { isGoodXHRStatus } from '../templates/Challenges/utils';
|
import { isGoodXHRStatus } from '../templates/Challenges/utils';
|
||||||
import postUpdate$ from '../templates/Challenges/utils/postUpdate$';
|
import postUpdate$ from '../templates/Challenges/utils/postUpdate$';
|
||||||
import { actionTypes } from './action-types';
|
import { actionTypes } from './action-types';
|
||||||
|
@ -13,7 +13,7 @@ import { createStructuredSelector } from 'reselect';
|
|||||||
|
|
||||||
// Local Utilities
|
// Local Utilities
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import { challengeTypes } from '../../../../utils/challengeTypes';
|
import { challengeTypes } from '../../../../utils/challenge-types';
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
import {
|
import {
|
||||||
ChallengeNodeType,
|
ChallengeNodeType,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
backEndProject,
|
backEndProject,
|
||||||
frontEndProject,
|
frontEndProject,
|
||||||
pythonProject
|
pythonProject
|
||||||
} from '../../../../utils/challengeTypes';
|
} from '../../../../utils/challenge-types';
|
||||||
import { Form } from '../../../components/formHelpers';
|
import { Form } from '../../../components/formHelpers';
|
||||||
|
|
||||||
interface SubmitProps {
|
interface SubmitProps {
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
finalize
|
finalize
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes';
|
import { challengeTypes, submitTypes } from '../../../../utils/challenge-types';
|
||||||
import {
|
import {
|
||||||
userSelector,
|
userSelector,
|
||||||
isSignedInSelector,
|
isSignedInSelector,
|
||||||
|
@ -3,7 +3,7 @@ import { createAction, handleActions } from 'redux-actions';
|
|||||||
|
|
||||||
import { getLines } from '../../../../../utils/get-lines';
|
import { getLines } from '../../../../../utils/get-lines';
|
||||||
import { createPoly } from '../../../../../utils/polyvinyl';
|
import { createPoly } from '../../../../../utils/polyvinyl';
|
||||||
import { challengeTypes } from '../../../../utils/challengeTypes';
|
import { challengeTypes } from '../../../../utils/challenge-types';
|
||||||
import { completedChallengesSelector } from '../../../redux';
|
import { completedChallengesSelector } from '../../../redux';
|
||||||
import { getTargetEditor } from '../utils/getTargetEditor';
|
import { getTargetEditor } from '../utils/getTargetEditor';
|
||||||
import { actionTypes, ns } from './action-types';
|
import { actionTypes, ns } from './action-types';
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import frameRunnerData from '../../../../../config/client/frame-runner.json';
|
import frameRunnerData from '../../../../../config/client/frame-runner.json';
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import testEvaluatorData from '../../../../../config/client/test-evaluator.json';
|
import testEvaluatorData from '../../../../../config/client/test-evaluator.json';
|
||||||
import { challengeTypes } from '../../../../utils/challengeTypes';
|
import { challengeTypes } from '../../../../utils/challenge-types';
|
||||||
import { cssToHtml, jsToHtml, concatHtml } from '../rechallenge/builders.js';
|
import { cssToHtml, jsToHtml, concatHtml } from '../rechallenge/builders.js';
|
||||||
import { getTransformers } from '../rechallenge/transformers';
|
import { getTransformers } from '../rechallenge/transformers';
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const envData = require('../../config/env.json');
|
const envData = require('../../config/env.json');
|
@ -1,7 +1,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { dasherize } = require('../../../utils/slugs');
|
const { dasherize } = require('../../../utils/slugs');
|
||||||
|
|
||||||
const { viewTypes } = require('../challengeTypes');
|
const { viewTypes } = require('../challenge-types');
|
||||||
|
|
||||||
const backend = path.resolve(
|
const backend = path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
@ -42,21 +42,22 @@ const views = {
|
|||||||
// quiz: Quiz
|
// quiz: Quiz
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNextChallengePath = (node, index, nodeArray) => {
|
function getNextChallengePath(_node, index, nodeArray) {
|
||||||
const next = nodeArray[index + 1];
|
const next = nodeArray[index + 1];
|
||||||
return next ? next.node.fields.slug : '/learn';
|
return next ? next.node.fields.slug : '/learn';
|
||||||
};
|
}
|
||||||
|
|
||||||
const getPrevChallengePath = (node, index, nodeArray) => {
|
function getPrevChallengePath(_node, index, nodeArray) {
|
||||||
const prev = nodeArray[index - 1];
|
const prev = nodeArray[index - 1];
|
||||||
return prev ? prev.node.fields.slug : '/learn';
|
return prev ? prev.node.fields.slug : '/learn';
|
||||||
};
|
}
|
||||||
|
|
||||||
const getTemplateComponent = challengeType => views[viewTypes[challengeType]];
|
function getTemplateComponent(challengeType) {
|
||||||
|
return views[viewTypes[challengeType]];
|
||||||
|
}
|
||||||
|
|
||||||
exports.createChallengePages =
|
exports.createChallengePages = function (createPage) {
|
||||||
createPage =>
|
return function ({ node }, index, thisArray) {
|
||||||
({ node }, index, thisArray) => {
|
|
||||||
const {
|
const {
|
||||||
superBlock,
|
superBlock,
|
||||||
block,
|
block,
|
||||||
@ -69,7 +70,7 @@ exports.createChallengePages =
|
|||||||
// TODO: challengeType === 7 and isPrivate are the same, right? If so, we
|
// TODO: challengeType === 7 and isPrivate are the same, right? If so, we
|
||||||
// should remove one of them.
|
// should remove one of them.
|
||||||
|
|
||||||
return createPage({
|
createPage({
|
||||||
path: slug,
|
path: slug,
|
||||||
component: getTemplateComponent(challengeType),
|
component: getTemplateComponent(challengeType),
|
||||||
context: {
|
context: {
|
||||||
@ -86,35 +87,40 @@ exports.createChallengePages =
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.createBlockIntroPages = createPage => edge => {
|
|
||||||
const {
|
|
||||||
fields: { slug },
|
|
||||||
frontmatter: { block }
|
|
||||||
} = edge.node;
|
|
||||||
|
|
||||||
return createPage({
|
|
||||||
path: slug,
|
|
||||||
component: intro,
|
|
||||||
context: {
|
|
||||||
block: dasherize(block),
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.createSuperBlockIntroPages = createPage => edge => {
|
exports.createBlockIntroPages = function (createPage) {
|
||||||
const {
|
return function (edge) {
|
||||||
fields: { slug },
|
const {
|
||||||
frontmatter: { superBlock }
|
fields: { slug },
|
||||||
} = edge.node;
|
frontmatter: { block }
|
||||||
|
} = edge.node;
|
||||||
|
|
||||||
return createPage({
|
createPage({
|
||||||
path: slug,
|
path: slug,
|
||||||
component: superBlockIntro,
|
component: intro,
|
||||||
context: {
|
context: {
|
||||||
superBlock,
|
block: dasherize(block),
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.createSuperBlockIntroPages = function (createPage) {
|
||||||
|
return function (edge) {
|
||||||
|
const {
|
||||||
|
fields: { slug },
|
||||||
|
frontmatter: { superBlock }
|
||||||
|
} = edge.node;
|
||||||
|
|
||||||
|
createPage({
|
||||||
|
path: slug,
|
||||||
|
component: superBlockIntro,
|
||||||
|
context: {
|
||||||
|
superBlock,
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
};
|
};
|
@ -1,4 +1,4 @@
|
|||||||
const challengePageCreators = require('./challengePageCreator');
|
const challengePageCreators = require('./challenge-page-creator');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...challengePageCreators
|
...challengePageCreators
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
@ -11,10 +12,19 @@ import layoutSelector from './layout-selector';
|
|||||||
jest.mock('../../src/analytics');
|
jest.mock('../../src/analytics');
|
||||||
|
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
function getComponentNameAndProps(elementType, pathname) {
|
|
||||||
const shallow = new ShallowRenderer();
|
interface NameAndProps {
|
||||||
|
props: Record<string, unknown>;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
function getComponentNameAndProps(
|
||||||
|
elementType: React.JSXElementConstructor<never>,
|
||||||
|
pathname: string
|
||||||
|
): NameAndProps {
|
||||||
|
// eslint-disable-next-line testing-library/render-result-naming-convention
|
||||||
|
const shallow = ShallowRenderer.createRenderer();
|
||||||
const LayoutReactComponent = layoutSelector({
|
const LayoutReactComponent = layoutSelector({
|
||||||
element: { type: elementType },
|
element: { type: elementType, props: {}, key: '' },
|
||||||
props: {
|
props: {
|
||||||
location: {
|
location: {
|
||||||
pathname
|
pathname
|
||||||
@ -24,8 +34,13 @@ function getComponentNameAndProps(elementType, pathname) {
|
|||||||
shallow.render(<Provider store={store}>{LayoutReactComponent}</Provider>);
|
shallow.render(<Provider store={store}>{LayoutReactComponent}</Provider>);
|
||||||
const view = shallow.getRenderOutput();
|
const view = shallow.getRenderOutput();
|
||||||
return {
|
return {
|
||||||
props: view.props,
|
props: view.props as Record<string, unknown>,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
name: view.type.WrappedComponent.displayName
|
name: view.type.WrappedComponent.displayName
|
||||||
|
// TODO: Revisit this when react-test-renderer is replaced with
|
||||||
|
// react-testing-library
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CertificationLayout,
|
CertificationLayout,
|
||||||
DefaultLayout
|
DefaultLayout
|
||||||
@ -6,21 +7,14 @@ import {
|
|||||||
import FourOhFourPage from '../../src/pages/404';
|
import FourOhFourPage from '../../src/pages/404';
|
||||||
import { isChallenge } from '../../src/utils/path-parsers';
|
import { isChallenge } from '../../src/utils/path-parsers';
|
||||||
|
|
||||||
interface Location {
|
|
||||||
pathname: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LayoutSelectorProps {
|
interface LayoutSelectorProps {
|
||||||
props: {
|
element: JSX.Element;
|
||||||
location: Location;
|
props: { location: { pathname: string } };
|
||||||
};
|
|
||||||
element: React.ReactElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function layoutSelector({
|
export default function layoutSelector({
|
||||||
element,
|
element,
|
||||||
props
|
props
|
||||||
}: LayoutSelectorProps): React.ReactElement {
|
}: LayoutSelectorProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
location: { pathname }
|
location: { pathname }
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
exports.isBrowser = function isBrowser() {
|
|
||||||
return typeof window !== 'undefined';
|
|
||||||
};
|
|
3
client/utils/index.ts
Normal file
3
client/utils/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isBrowser(): boolean {
|
||||||
|
return typeof window !== 'undefined';
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import { injectConditionalTags } from './tags';
|
import { injectConditionalTags } from './tags';
|
||||||
|
|
||||||
describe('Tags', () => {
|
describe('Tags', () => {
|
||||||
it('injectConditionalTags should inject gap dev homelocation', () => {
|
it('injectConditionalTags should inject gap dev homelocation', () => {
|
||||||
let injectedTags = injectConditionalTags(
|
const injectedTags = injectConditionalTags(
|
||||||
[],
|
[],
|
||||||
'https://www.freecodecamp.dev'
|
'https://www.freecodecamp.dev'
|
||||||
);
|
);
|
||||||
@ -10,7 +11,7 @@ describe('Tags', () => {
|
|||||||
expect(injectedTags[0].props.id === 'gap-dev').toBeTruthy();
|
expect(injectedTags[0].props.id === 'gap-dev').toBeTruthy();
|
||||||
});
|
});
|
||||||
it('injectConditionalTags should inject gap for english homeLocation', () => {
|
it('injectConditionalTags should inject gap for english homeLocation', () => {
|
||||||
let injectedTags = injectConditionalTags(
|
const injectedTags = injectConditionalTags(
|
||||||
[],
|
[],
|
||||||
'https://www.freecodecamp.org'
|
'https://www.freecodecamp.org'
|
||||||
);
|
);
|
||||||
@ -18,7 +19,7 @@ describe('Tags', () => {
|
|||||||
expect(injectedTags[0].props.id === 'gap-org').toBeTruthy();
|
expect(injectedTags[0].props.id === 'gap-org').toBeTruthy();
|
||||||
});
|
});
|
||||||
it('injectConditionalTags should inject gap for espanol homeLocation', () => {
|
it('injectConditionalTags should inject gap for espanol homeLocation', () => {
|
||||||
let injectedTags = injectConditionalTags(
|
const injectedTags = injectConditionalTags(
|
||||||
[],
|
[],
|
||||||
'https://www.freecodecamp.org/espanol'
|
'https://www.freecodecamp.org/espanol'
|
||||||
);
|
);
|
||||||
@ -26,7 +27,7 @@ describe('Tags', () => {
|
|||||||
expect(injectedTags[0].props.id === 'gap-org').toBeTruthy();
|
expect(injectedTags[0].props.id === 'gap-org').toBeTruthy();
|
||||||
});
|
});
|
||||||
it('injectConditionalTags should inject cap and chinese gap for chinese homeLocation', () => {
|
it('injectConditionalTags should inject cap and chinese gap for chinese homeLocation', () => {
|
||||||
let injectedTags = injectConditionalTags(
|
const injectedTags = injectConditionalTags(
|
||||||
[],
|
[],
|
||||||
'https://chinese.freecodecamp.org'
|
'https://chinese.freecodecamp.org'
|
||||||
);
|
);
|
||||||
@ -35,7 +36,7 @@ describe('Tags', () => {
|
|||||||
expect(injectedTags[1].props.id === 'gap-org-chinese').toBeTruthy();
|
expect(injectedTags[1].props.id === 'gap-org-chinese').toBeTruthy();
|
||||||
});
|
});
|
||||||
it('injectConditionalTags should not inject tags for localhost homeLocation', () => {
|
it('injectConditionalTags should not inject tags for localhost homeLocation', () => {
|
||||||
let injectedTags = injectConditionalTags([], 'http://localhost:8000/');
|
const injectedTags = injectConditionalTags([], 'http://localhost:8000/');
|
||||||
expect(injectedTags.length === 0).toBeTruthy();
|
expect(injectedTags.length === 0).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -6,13 +6,13 @@ import env from '../../config/env.json';
|
|||||||
|
|
||||||
const { homeLocation } = env;
|
const { homeLocation } = env;
|
||||||
|
|
||||||
export const getheadTagComponents = () => {
|
export function getheadTagComponents(): JSX.Element[] {
|
||||||
const socialImage =
|
const socialImage =
|
||||||
'https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png';
|
'https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png';
|
||||||
|
|
||||||
const pathToBootstrap = withPrefix('/css/bootstrap.min.css');
|
const pathToBootstrap = withPrefix('/css/bootstrap.min.css');
|
||||||
|
|
||||||
let headTags = [
|
const headTags = [
|
||||||
<link
|
<link
|
||||||
as='style'
|
as='style'
|
||||||
href={pathToBootstrap}
|
href={pathToBootstrap}
|
||||||
@ -54,19 +54,25 @@ export const getheadTagComponents = () => {
|
|||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
return injectConditionalTags(headTags, homeLocation);
|
return injectConditionalTags(headTags, homeLocation);
|
||||||
};
|
}
|
||||||
|
|
||||||
// strips subpath and protocol
|
// strips subpath and protocol
|
||||||
|
|
||||||
export const injectConditionalTags = (tagsArray, homeLocation) => {
|
export function injectConditionalTags(
|
||||||
|
tagsArray: JSX.Element[],
|
||||||
|
homeLocation: string
|
||||||
|
): JSX.Element[] {
|
||||||
if (homeLocation.includes('localhost')) return tagsArray;
|
if (homeLocation.includes('localhost')) return tagsArray;
|
||||||
|
|
||||||
const parsedHomeUrl = psl.parse(new URL(homeLocation).host);
|
const parsedHomeUrl = psl.parse(
|
||||||
|
new URL(homeLocation).host
|
||||||
|
) as psl.ParsedDomain;
|
||||||
|
|
||||||
// inject gap all production languages except Chinese
|
// inject gap all production languages except Chinese
|
||||||
if (parsedHomeUrl.subdomain === 'www' && parsedHomeUrl.tld === 'org') {
|
if (parsedHomeUrl.subdomain === 'www' && parsedHomeUrl.tld === 'org') {
|
||||||
tagsArray.push(
|
tagsArray.push(
|
||||||
<script
|
<script
|
||||||
|
// @ts-expect-error TODO: check use of href/rel on <script>
|
||||||
href={withPrefix('/misc/gap-org.js')}
|
href={withPrefix('/misc/gap-org.js')}
|
||||||
id='gap-org'
|
id='gap-org'
|
||||||
key='gap-org'
|
key='gap-org'
|
||||||
@ -79,6 +85,7 @@ export const injectConditionalTags = (tagsArray, homeLocation) => {
|
|||||||
if (parsedHomeUrl.subdomain === 'www' && parsedHomeUrl.tld === 'dev') {
|
if (parsedHomeUrl.subdomain === 'www' && parsedHomeUrl.tld === 'dev') {
|
||||||
tagsArray.push(
|
tagsArray.push(
|
||||||
<script
|
<script
|
||||||
|
// @ts-expect-error See above
|
||||||
href={withPrefix('/misc/gap-dev.js')}
|
href={withPrefix('/misc/gap-dev.js')}
|
||||||
id='gap-dev'
|
id='gap-dev'
|
||||||
key='gap-dev'
|
key='gap-dev'
|
||||||
@ -90,13 +97,15 @@ export const injectConditionalTags = (tagsArray, homeLocation) => {
|
|||||||
// inject cap and Chinese gap for production Chinese
|
// inject cap and Chinese gap for production Chinese
|
||||||
if (parsedHomeUrl.subdomain === 'chinese' && parsedHomeUrl.tld === 'org') {
|
if (parsedHomeUrl.subdomain === 'chinese' && parsedHomeUrl.tld === 'org') {
|
||||||
tagsArray.push(
|
tagsArray.push(
|
||||||
<scripts
|
<script
|
||||||
|
// @ts-expect-error See above
|
||||||
href={withPrefix('/misc/cap.js')}
|
href={withPrefix('/misc/cap.js')}
|
||||||
id='cap'
|
id='cap'
|
||||||
key='cap'
|
key='cap'
|
||||||
rel='stylesheet'
|
rel='stylesheet'
|
||||||
/>,
|
/>,
|
||||||
<script
|
<script
|
||||||
|
// @ts-expect-error See above
|
||||||
href={withPrefix('/misc/gap-org-chinese.js')}
|
href={withPrefix('/misc/gap-org-chinese.js')}
|
||||||
id='gap-org-chinese'
|
id='gap-org-chinese'
|
||||||
key='gap-org-chinese'
|
key='gap-org-chinese'
|
||||||
@ -105,10 +114,10 @@ export const injectConditionalTags = (tagsArray, homeLocation) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return tagsArray;
|
return tagsArray;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getPostBodyComponents = pathname => {
|
export function getPostBodyComponents(pathname: string): JSX.Element[] {
|
||||||
let scripts = [];
|
const scripts = [];
|
||||||
const mathJaxScriptElement = (
|
const mathJaxScriptElement = (
|
||||||
<script
|
<script
|
||||||
async={false}
|
async={false}
|
||||||
@ -127,4 +136,4 @@ export const getPostBodyComponents = pathname => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return scripts.filter(Boolean);
|
return scripts.filter(Boolean);
|
||||||
};
|
}
|
@ -4,7 +4,7 @@ const util = require('util');
|
|||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
const { findIndex, reduce, toString } = require('lodash');
|
const { findIndex, reduce, toString } = require('lodash');
|
||||||
const readDirP = require('readdirp');
|
const readDirP = require('readdirp');
|
||||||
const { helpCategoryMap } = require('../client/utils/challengeTypes');
|
const { helpCategoryMap } = require('../client/utils/challenge-types');
|
||||||
const { showUpcomingChanges } = require('../config/env.json');
|
const { showUpcomingChanges } = require('../config/env.json');
|
||||||
const { curriculum: curriculumLangs } =
|
const { curriculum: curriculumLangs } =
|
||||||
require('../config/i18n/all-langs').availableLangs;
|
require('../config/i18n/all-langs').availableLangs;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const Joi = require('joi');
|
const Joi = require('joi');
|
||||||
Joi.objectId = require('joi-objectid')(Joi);
|
Joi.objectId = require('joi-objectid')(Joi);
|
||||||
|
|
||||||
const { challengeTypes } = require('../../client/utils/challengeTypes');
|
const { challengeTypes } = require('../../client/utils/challenge-types');
|
||||||
|
|
||||||
const slugRE = new RegExp('^[a-z0-9-]+$');
|
const slugRE = new RegExp('^[a-z0-9-]+$');
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ const {
|
|||||||
const {
|
const {
|
||||||
default: createWorker
|
default: createWorker
|
||||||
} = require('../../client/src/templates/Challenges/utils/worker-executor');
|
} = require('../../client/src/templates/Challenges/utils/worker-executor');
|
||||||
const { challengeTypes } = require('../../client/utils/challengeTypes');
|
const { challengeTypes } = require('../../client/utils/challenge-types');
|
||||||
// the config files are created during the build, but not before linting
|
// the config files are created during the build, but not before linting
|
||||||
/* eslint-disable import/no-unresolved */
|
/* eslint-disable import/no-unresolved */
|
||||||
const testEvaluator =
|
const testEvaluator =
|
||||||
|
@ -39,7 +39,7 @@ The practice projects have some additional tooling to help create new projects a
|
|||||||
---
|
---
|
||||||
id: Unique identifier (alphanumerical, MongoDB_id)
|
id: Unique identifier (alphanumerical, MongoDB_id)
|
||||||
title: 'Challenge Title'
|
title: 'Challenge Title'
|
||||||
challengeType: Integer, defined in `client/utils/challengeTypes.js`
|
challengeType: Integer, defined in `client/utils/challenge-types.js`
|
||||||
videoUrl: 'url of video explanation'
|
videoUrl: 'url of video explanation'
|
||||||
forumTopicId: 12345
|
forumTopicId: 12345
|
||||||
---
|
---
|
||||||
@ -488,6 +488,6 @@ Once you have verified that each challenge you've worked on passes the tests, [p
|
|||||||
|
|
||||||
Creating and Editing Challenges:
|
Creating and Editing Challenges:
|
||||||
|
|
||||||
1. [Challenge types](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/utils/challengeTypes.js#L1-L13) - what the numeric challenge type values mean (enum).
|
1. [Challenge types](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/utils/challenge-types.js#L1-L13) - what the numeric challenge type values mean (enum).
|
||||||
|
|
||||||
2. [Contributing to FreeCodeCamp - Writing ES6 Challenge Tests](https://www.youtube.com/watch?v=iOdD84OSfAE#t=2h49m55s) - a video following [Ethan Arrowood](https://twitter.com/ArrowoodTech) as he contributes to the old version of the curriculum.
|
2. [Contributing to FreeCodeCamp - Writing ES6 Challenge Tests](https://www.youtube.com/watch?v=iOdD84OSfAE#t=2h49m55s) - a video following [Ethan Arrowood](https://twitter.com/ArrowoodTech) as he contributes to the old version of the curriculum.
|
||||||
|
Reference in New Issue
Block a user