diff --git a/README.md b/README.md index a694a73060a..1da4d1ba3da 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,22 @@ > Short code snippets for all your development needs -* Visit [the website](https://30secondsofcode.org) to view our snippet collection. -* Search for snippets that suit your needs. You can search by name, tag, language or using a snippet's description. Just start typing a term and see what comes up. +* Visit [the website](https://30secondsofcode.org) to view the snippet collection. +* Search for snippets and collections that suit your needs, using the name, tags, language or description. * Browse all [snippets](https://30secondsofcode.org/snippets/p/1) or individual [snippet collections](https://30secondsofcode.org/collections/p/1) for each topic. * Click on each snippet card to view the whole snippet, including code, explanation and examples. * You can copy code blocks on any snippet card, using the copy button at the top right. -* If you like the project, give it a star. It means a lot to the people maintaining it. +* If you like the project, give it a star. It means a lot. ## Want to contribute? -* If you want to help us improve, take a minute to read the [Contribution Guidelines](/CONTRIBUTING.md) first. -* Use the relevant `template.md` to add new snippets to each collection. -* If you find a problem with a specific snippet, please [open an issue](https://github.com/30-seconds/30-seconds-of-code/issues/new). -* If you find a problem with the website, please [open an issue](https://github.com/30-seconds/30-seconds-of-code/issues/new). +* If you want to help, take a minute to read the [Contribution Guidelines](/CONTRIBUTING.md) first. +* If you want to discuss a new snippet idea, first you should [create a discussion](https://github.com/Chalarangelo/30-seconds-of-code/discussions/new?category=ideas). +* If you find a problem with a specific snippet or the website, please [open an issue](https://github.com/30-seconds/30-seconds-of-code/issues/new). ## Credits * This repository is maintained by [Angelos Chalaris]([https://github.com/30-seconds](https://github.com/Chalarangelo)). -* All snippets are licensed under the CC-BY-4.0 License, unless explicitly stated otherwise. +* All snippets are licensed under the [CC-BY-4.0 License](https://creativecommons.org/licenses/by/4.0/), unless explicitly stated otherwise. * Logos, names and trademarks are not to be used without the explicit consent of the owner. * The website is powered by [Netlify](https://www.netlify.com/), [Astro](https://astro.build/) & [GitHub](https://github.com/). diff --git a/assets/icons.woff2 b/assets/icons.woff2 index 8f6cda9294c..01986a80143 100644 Binary files a/assets/icons.woff2 and b/assets/icons.woff2 differ diff --git a/content/languages/css.yaml b/content/languages/css.yaml index f5d1d05c016..830b6d2ad61 100644 --- a/content/languages/css.yaml +++ b/content/languages/css.yaml @@ -1,3 +1,4 @@ short: css long: css name: CSS +additionalReferences: [js, html] diff --git a/content/languages/html.yaml b/content/languages/html.yaml index 7ca201b4efd..608a753dae6 100644 --- a/content/languages/html.yaml +++ b/content/languages/html.yaml @@ -1,3 +1,4 @@ short: html long: html name: HTML +additionalReferences: [css, js] diff --git a/content/languages/javascript.yaml b/content/languages/javascript.yaml index 5fe1eb6ff79..902ca8b134e 100644 --- a/content/languages/javascript.yaml +++ b/content/languages/javascript.yaml @@ -1,6 +1,7 @@ short: js long: javascript name: JavaScript +additionalReferences: [css, html] references: ...: >- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax diff --git a/content/languages/react.yaml b/content/languages/react.yaml index 606a98485f7..a50f26a11c3 100644 --- a/content/languages/react.yaml +++ b/content/languages/react.yaml @@ -1,6 +1,7 @@ short: jsx long: react name: React +additionalReferences: [css, html, js] references: useCallback(): https://react.dev/reference/react/useCallback useContext(): https://react.dev/reference/react/useContext diff --git a/src/blocks/extractor/extractor.js b/src/blocks/extractor/extractor.js index 5d522b80592..2fb9b0cbe52 100644 --- a/src/blocks/extractor/extractor.js +++ b/src/blocks/extractor/extractor.js @@ -1,6 +1,5 @@ /* eslint-disable no-unused-vars */ import pathSettings from '#settings/paths'; -import JSX_SNIPPET_PRESETS from '#settings/jsxSnippetPresets'; import { Logger } from '#blocks/utilities/logger'; import { TextParser } from '#blocks/extractor/textParser'; import { MarkdownParser } from '#blocks/extractor/markdownParser'; @@ -9,10 +8,6 @@ import { YAMLHandler } from '#blocks/utilities/yamlHandler'; import { stripMarkdownFormat } from '#utils'; const mdCodeFence = '```'; -const codeMatcher = new RegExp( - `${mdCodeFence}.*\r?\n(?[\\S\\s]*?)${mdCodeFence}`, - 'g' -); const { rawContentPath: contentDir } = pathSettings; @@ -89,20 +84,27 @@ export class Extractor { const languageData = YAMLHandler.fromGlob( `${contentDir}/languages/*.yaml` ).reduce((acc, language) => { - const { short, long, name, references = {} } = language; + const { + short, + long, + name, + references = {}, + additionalReferences = [], + } = language; acc.set(long, { id: long, long, short, name, references: new Map(Object.entries(references)), + allLanguageReferences: [short, ...additionalReferences], }); return acc; }, new Map()); logger.success('Finished extracting language data'); Extractor.languageData = languageData; Extractor.data.languages = [...languageData].map(([id, data]) => { - const { references, ...restData } = data; + const { references, allLanguageReferences, ...restData } = data; return { ...restData }; }); MarkdownParser.loadLanguageData([...Extractor.languageData.values()]); @@ -193,20 +195,7 @@ export class Extractor { unlisted, } = snippet; - // This check might be overkill, but better safe than sorry - const isBlog = type !== 'snippet' && filePath.includes('/articles/'); - const isCSS = filePath.includes('/css/') && type === 'snippet'; - const isReact = filePath.includes('/react/') && type === 'snippet'; - const language = Extractor.languageData.get(languageKey) || undefined; - const languageKeys = isBlog - ? [] - : isCSS - ? ['js', 'html', 'css'] - : isReact - ? ['js', 'jsx'] - : [language]; - const id = filePath.replace(`${contentDir}/snippets/`, '').slice(0, -3); const tags = rawTags.map(tag => tag.toLowerCase()); @@ -225,49 +214,12 @@ export class Extractor { logger.warn(`Snippet ${id} has a long SEO description.`); } - let code = null; - - if (isCSS || isReact) { - const codeBlocks = [...body.matchAll(codeMatcher)].map(v => - v.groups.code.trim() - ); - - if (isCSS) { - code = { - html: codeBlocks[0], - css: codeBlocks[1], - js: codeBlocks[2] || '', - }; - } - - if (isReact) { - code = - codeBlocks.length > 2 - ? { - js: `${codeBlocks[1]}\n\n${codeBlocks[2]}`, - css: codeBlocks[0], - } - : { - js: `${codeBlocks[0]}\n\n${codeBlocks[1]}`, - css: '', - }; - /* eslint-disable camelcase */ - code = { - ...code, - html: JSX_SNIPPET_PRESETS.envHtml, - js_pre_processor: JSX_SNIPPET_PRESETS.jsPreProcessor, - js_external: JSX_SNIPPET_PRESETS.jsImports.join(';'), - }; - /* eslint-enable camelcase */ - } - } - const html = MarkdownParser.parseSegments( { fullDescription: fullText, description: shortText, }, - languageKeys + language ? language.allLanguageReferences : [] ); return { @@ -282,7 +234,6 @@ export class Extractor { shortText, fullText, ...html, - code, cover, seoDescription, language: languageKey, diff --git a/src/blocks/models/snippet.js b/src/blocks/models/snippet.js index 6ef45eed9e1..582d5417cd3 100644 --- a/src/blocks/models/snippet.js +++ b/src/blocks/models/snippet.js @@ -18,7 +18,6 @@ export const snippet = { fullText: 'string', descriptionHtml: 'string', fullDescriptionHtml: 'string', - code: 'object', cover: 'string', seoDescription: 'string', }, @@ -73,9 +72,6 @@ export const snippet = { body: snippet => convertToSeoSlug(snippet.fileName.slice(0, -3)), cache: true, }, - url: snippet => - `https://github.com/Chalarangelo/30-seconds-of-code/blob/master/content/snippets${snippet.slug}.md`, - actionType: snippet => (snippet.code ? 'codepen' : undefined), isScheduled: { body: snippet => snippet.dateModified > new Date(), cache: true, diff --git a/src/blocks/serializers/snippetContextSerializer.js b/src/blocks/serializers/snippetContextSerializer.js index 94bd16d4eca..b16867fee71 100644 --- a/src/blocks/serializers/snippetContextSerializer.js +++ b/src/blocks/serializers/snippetContextSerializer.js @@ -11,12 +11,9 @@ export const snippetContextSerializer = { attributes: [ 'title', 'fullDescription', - 'url', 'slug', ['dateFormatted', 'date'], ['formattedTags', 'tags'], - 'actionType', - 'code', ['coverUrl', 'cover'], ], }; diff --git a/src/blocks/utilities/preparedQueries.js b/src/blocks/utilities/preparedQueries.js index d32de6e43f2..631f704187a 100644 --- a/src/blocks/utilities/preparedQueries.js +++ b/src/blocks/utilities/preparedQueries.js @@ -35,6 +35,9 @@ const coverAssetPath = 'content/assets/cover'; // Redirects constants const redirectsPath = 'content/redirects.yaml'; +// Collection config constants +const contentConfigsGlob = 'content/collections/**/*.yaml'; + // Performance constants (manually import Pages.csv from Google Search Console) const performancePath = 'imported/Pages.csv'; @@ -307,4 +310,32 @@ export class PreparedQueries { heading: headingMatcher.test(fullText), }; }); + + /** + * Returns an object with information about references with the same code + * identifier in multiple languages. + * @param {string[]} languageKeys - The language keys to get references for. + */ + static duplicateReferences = + () => + (languageKeys = ['js', 'css', 'html', 'jsx']) => + withCache(`duplicateReferences#${languageKeys.join(',')}`, () => { + const referenceMap = new Map(); + YAMLHandler.fromGlob(contentConfigsGlob).forEach(config => { + const { short, references } = config; + if (!references || !references.length) return; + references.forEach(reference => { + const referenceLanguages = referenceMap.get(reference) || []; + referenceLanguages.push(short); + referenceMap.set(reference, referenceLanguages); + }); + }); + return [...referenceMap.entries()].reduce( + (acc, [reference, languages]) => { + if (languages.length > 1) acc[reference] = languages; + return acc; + }, + {} + ); + }); } diff --git a/src/components/SnippetCard.astro b/src/components/SnippetCard.astro index a381e4d8569..298618ab7b0 100644 --- a/src/components/SnippetCard.astro +++ b/src/components/SnippetCard.astro @@ -23,20 +23,5 @@ const { snippet } = Astro.props; class='card-description flex flex-col' set:html={snippet.fullDescription} /> -
- { - snippet.actionType === 'codepen' && ( -
diff --git a/src/components/SnippetScripts.astro b/src/components/SnippetScripts.astro deleted file mode 100644 index 4b75708d74f..00000000000 --- a/src/components/SnippetScripts.astro +++ /dev/null @@ -1,60 +0,0 @@ ---- -const { snippet } = Astro.props; - -const snippetCodeData = JSON.stringify(snippet.code); ---- - - -{ - snippetCodeData && snippetCodeData.length ? ( - diff --git a/src/prefabs/index.js b/src/prefabs/index.js index fe18c51c4be..8527e2df1e7 100644 --- a/src/prefabs/index.js +++ b/src/prefabs/index.js @@ -13,7 +13,7 @@ const cardBase = 'card mt-7 mx-1 mb-4 md:mx-3.5 srfc-01db txt-100 br-lg'; const cardTitleBase = 'txt-200 fs-lg md:fs-xl f-alt f-ellipsis lh-tight'; const cards = { - snippetCard: `${cardBase} snippet-card g-c2 px-4 pt-6 pb-4 md:px-6 md:pb-6`, + snippetCard: `${cardBase} snippet-card g-c2 px-4 py-6 pb-4 md:p-6`, previewCard: `${cardBase} list-card grid a-center py-5 px-4 md:p-6 relative no-overflow`, simpleCard: `${cardBase} px-4 py-6 md:px-6 md:py-8`, cardTitle: `${cardTitleBase} mt-0 mx-0 mb-1`, diff --git a/src/settings/jsxSnippetPresets.js b/src/settings/jsxSnippetPresets.js deleted file mode 100644 index ccf6caa0307..00000000000 --- a/src/settings/jsxSnippetPresets.js +++ /dev/null @@ -1,8 +0,0 @@ -export default { - envHtml: '
', - jsPreProcessor: 'babel', - jsImports: [ - 'https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js', - 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js', - ], -}; diff --git a/src/settings/settings.js b/src/settings/settings.js index b1491d4224d..bc801694120 100644 --- a/src/settings/settings.js +++ b/src/settings/settings.js @@ -1,7 +1,6 @@ import global from './global.js'; import paths from './paths.js'; import searchEngine from './searchEngine.js'; -import jsxSnippetPresets from './jsxSnippetPresets.js'; import presentation from './presentation.js'; import tags from './tags.js'; @@ -9,7 +8,6 @@ export default { global, paths, searchEngine, - jsxSnippetPresets, presentation, tags, }; diff --git a/src/styles/_icons.scss b/src/styles/_icons.scss index ac4edb89fb2..e879f7434b4 100644 --- a/src/styles/_icons.scss +++ b/src/styles/_icons.scss @@ -1,6 +1,6 @@ @font-face { font-family: "icons"; - src: url("../../assets/icons.woff2?a276e0d037d19a56c4c2d0a344b014af") format("woff2"); + src: url("../../assets/icons.woff2?6d06981837ab189b57dcb460e6e465cd") format("woff2"); } .icon:before { @@ -14,30 +14,24 @@ -moz-osx-font-smoothing: grayscale; } -.icon.icon-check:before { +.icon.icon-search:before { content: "\f101"; } -.icon.icon-chevron-left:before { +.icon.icon-home:before { content: "\f102"; } -.icon.icon-chevron-right:before { +.icon.icon-close:before { content: "\f103"; } .icon.icon-clipboard:before { content: "\f104"; } -.icon.icon-close:before { +.icon.icon-chevron-right:before { content: "\f105"; } -.icon.icon-codepen:before { +.icon.icon-chevron-left:before { content: "\f106"; } -.icon.icon-github:before { +.icon.icon-check:before { content: "\f107"; } -.icon.icon-home:before { - content: "\f108"; -} -.icon.icon-search:before { - content: "\f109"; -} diff --git a/src/styles/_layout.scss b/src/styles/_layout.scss index 38c699ab6b5..e0b4d7b8179 100644 --- a/src/styles/_layout.scss +++ b/src/styles/_layout.scss @@ -14,9 +14,6 @@ .j-center { justify-content: center; } -.j-space-evenly { - justify-content: space-evenly; -} .a-center { align-items: center; } diff --git a/src/styles/_print.scss b/src/styles/_print.scss index 64241af8b30..e2a1fda77d8 100644 --- a/src/styles/_print.scss +++ b/src/styles/_print.scss @@ -43,7 +43,6 @@ // Hide UI header, .breadcrumbs, - .card-actions, footer, .recommendation-list-title, .recommendation-list-title + ul { diff --git a/src/styles/components/_snippet-card.scss b/src/styles/components/_snippet-card.scss index d39cb06872d..6aec178efa6 100644 --- a/src/styles/components/_snippet-card.scss +++ b/src/styles/components/_snippet-card.scss @@ -31,10 +31,6 @@ } } - &:last-child { - margin-block-end: 1.5rem; - } - > pre { padding-block-end: 1.5rem; } @@ -50,10 +46,6 @@ } } } - - &:last-of-type > pre { - padding-block-end: 1.5rem; - } } }